Promise简介
是一个保留了异步事件将来执行后果的对象。它不是一个新技术,而是一种能够优化异步编程编码格调的标准。最早诞生于社区,用于解决JavaScript代码中回调嵌套层级过多的问题和错误处理逻辑堆砌的问题。应用Promise对象封装异步操作,能够让代码变得直观、线性、优雅。
Promise根本应用
用Promise封装Ajax申请
//先写一个原始的Ajax申请let xhr = new XMLHttpRequest()function resolve(v){console.log(v);}function reject(e){console.log(e);}xhr.onerror = function(e){ reject(e)}xhr.ontimeout = function(e){ reject(e)}xhr.onreadystatechange = function(){ if(xhr.readyState===4){ if(xhr.status===200){ resolve(xhr.response) } }}xhr.open('Get','https://wwww.google.com',true)xhr.send()// 第一版:利用Promise封装ajaxlet p = new Promise((resolve,reject)=>{ let xhr = new XMLHttpRequest() xhr.onerror = function(e){ reject(e) } xhr.ontimeout = function(e){ reject(e) } xhr.onreadystatechange = function(){ if(xhr.readyState===4){ if(xhr.status===200){ resolve(xhr.response) } } } xhr.open('Get','https://wwww.google.com',true) xhr.send()});p.then(v=>{ console.log("我是胜利时注册的回调");},e=>{ console.log("我是失败时注册的回调");})// 第二版 反对传参、封装了Promise创立的细节function Xpromise(request){ function executor(request,resolve,reject){ let xhr = new XMLHttpRequest() xhr.onerror = function(e){ reject(e) } xhr.ontimeout = function(e){ reject(e) } xhr.onreadystatechange = function(){ if(xhr.readyState===4){ if(xhr.status===200){ resolve(xhr.response) } } } xhr.open('Get',request.url,true) xhr.send() } renturn new Promise(executor);}let x1 = Xpromise(makeRequest('https://wwww.google.com')).then(v=>{ console.log("我是胜利时注册的回调");},e=>{ console.log("我是失败时注册的回调");})
另外,Promise还提供了一系列好用的API,如动态resolve()、all()、race()办法等。
实现原理概述
Promise用回调函数提早绑定、回调函数onResolve返回值穿透机制解决回调嵌套层级过多的问题;应用谬误冒泡机制简化了错误处理逻辑堆砌的问题。
手工实现一个Promise
第一版
// 第一点:Promise是一个类class MyPromise { // 第二点:Promised构造函数的参数是一个函数; constructor(fn) { if (typeof fn !== "function") { throw new Error("promise的结构函数参数应该为函数类型") } // 第三点:Promise的外部状态有三个,Promise对象具备值 this._status = PENDING; this._value = undefined; // 第五点:new Promise(fn)时,就须要执行fn做业务逻辑,故构造函数里就要调用fn函数。此处外部函数_resolve和_reject会被调用用于追踪Promise的外部状态 try { //留神用try-catch包住 fn(this._resolve.bind(this), this._reject.bind(this)); } catch (err) { this._reject(err) } } // 第四点:定义MyPromise状态翻转时,要执行的外部函数 _resolve(val){ if (this._status !== this.PENDING) return //这段代码体现了Promise的状态翻转:只能是P->F或者是P->R this._status = FULLFILLED; this._value = val; }; _reject(err){ if (this._status !== this.PENDING) return this._status = REJECTED; this._value = err; };}
第二版
class MyPromise { constructor(fn) { if (typeof fn !== "function") { throw new Error("myPromise的结构函数参数应该为函数类型") } this._status = PENDING; this._value = undefined; //个性2-新增定义两个回调函数数组 this.fulfilledQueue = []; this.rejectedQueue = []; try { fn(this._resolve.bind(this), this._reject.bind(this)); } catch (err) { this._reject(err) } } _resolve(val){ if (this._status !== this.PENDING) return // 个性4:注册的回调函数在Promise状态翻转时会执行,执行的形式是循环从队列外面取出回调执行 // 定义run函数 run = (value)=>{ this._status = FULLFILLED; this._value = val; let ck; while(ck = this.fulfilledQueue.shift()){ ck(value); } } // 个性5:run()函数的执行,这里十分要害。要把run放进setTimeOut。为什么? // 因为执行_resolve()函数时,then()可能还没执行,所以为了让then()中的回调、包含链式调用的then()的回调增加到fulfilledQueue中, // 须要提早执行run()。实际上这里用setTimeout性能差,理论中采纳微工作的形式实现 setTimeout(run,0) //run(); }; _reject(err){ if (this._status !== this.PENDING) return run = (error)=>{ this._status = this.REJECTED; this._value = err; let ck; while(ck = this.rejectedQueue.shift()){ ck(error) } } setTimeout(run,0); }; // 最重要的then函数: // 个性1-用于注册回调,then()函数有两个参数,都是可选的,如果参数不是函数将会被疏忽 // then()反对被同一个Promise屡次注册,个性2-所以Promise外部要保护两个数组,别离存储then上注册的胜利回调和失败回调); // then()反对链式调用,个性3-之所以反对是因为其返回值是一个新的Promise,此处要实现回调函数onFulfilled穿透机制的实现 //个性1-回调函数的注册:故为它传递两个回调函数占位 then(onFulfilled,onRejected){ const {_status, _value} = this; //个性3-返回一个新的promise return new MyPromise((onFulfilledNext, onRejectedNext)=>{ let fulfilled = value => { try{ if(typeof onFulfilled != "function"){ //如果 onFulfilled 或 onRejected 不是函数,onFulfilled 或 onRejected 被疏忽 onFulfilledNext(value) }else{ //如果 onFulfilled 或者 onRejected 返回一个值 res let res = onFulfilled(value) if(res instanceof MyPromise){ //若 res 为 Promise ,这时后一个回调函数,就会期待该 Promise 对象(即res )的状态发生变化,才会被调用,并且新的 Promise 状态和 res 的状态雷同 res.then(onFulfilledNext, onRejectedNext) }else{ //若 res 不为 Promise ,则使res 间接作为新返回的 Promise 对象的值 onFulfilledNext(res) } } }catch(err){ onRejectedNext(err) } } let rejected = error => { try{ if(typeof onRejected != "function"){ onRejectedNext(error) }else{ let res = onRejectedNext(error) if(res instanceof MyPromise){ res.then(onFulfilledNext, onRejectedNext) }else{ onFulfilledNext(res); } } }catch(err){ onRejectedNext(err) } } switch(_status){ case PENDING: //在 promise 状态扭转前, onFulfilled或者onRejected不可被调用 this._fulfilledQueue.push(onFulfilled) this._rejectedQueue.push(onRejected) break case FULFILLED: //onFulfilled调用次数只有一次,fulfilled对onFulFilled进行包装。why need 包装?因为onFulFilled可能是函数、可能不是,如果是函数,返回值可能是Promise可能不是 fulfilled(_value) break case REJECTED: //onRejected调用次数只有一次 rejected(_value) break } }) }}
第三版
class MyPromise { constructor(handle) { if (!isFunction(handle)) { throw new Error('MyPromise must accept a function as a parameter') } this._status = PENDING this._value = undefined this._fulfilledQueues = [] this._rejectedQueues = [] try { handle(this._resolve.bind(this), this._reject.bind(this)) } catch (err) { this._reject(err) } } // 增加resovle时执行的函数 _resolve(val) { const run = () => { if (this._status !== PENDING) return const runFulfilled = (value) => { let cb; while (cb = this._fulfilledQueues.shift()) { cb(value) } } const runRejected = (error) => { let cb; while (cb = this._rejectedQueues.shift()) { cb(error) } } /* 如果resolve的参数为Promise对象,则必须期待该Promise对象状态扭转后, 以后Promsie的状态才会扭转,且状态取决于参数Promsie对象的状态, 因而这里进行逻辑辨别 */ if (val instanceof MyPromise) { val.then(value => { this._value = value this._status = FULFILLED runFulfilled(value) }, err => { this._value = err this._status = REJECTED runRejected(err) }) } else { this._value = val this._status = FULFILLED runFulfilled(val) } } setTimeout(run, 0) } _reject(err) { if (this._status !== PENDING) return const run = () => { this._status = REJECTED this._value = err let cb; while (cb = this._rejectedQueues.shift()) { cb(err) } } setTimeout(run, 0) } then(onFulfilled, onRejected) { const { _value, _status } = this return new MyPromise((onFulfilledNext, onRejectedNext) => { let fulfilled = value => { try { if (!isFunction(onFulfilled)) { onFulfilledNext(value) } else { let res = onFulfilled(value); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext) } else { onFulfilledNext(res) } } } catch (err) { onRejectedNext(err) } } let rejected = error => { try { if (!isFunction(onRejected)) { onRejectedNext(error) } else { let res = onRejected(error); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext) } else { onFulfilledNext(res) } } } catch (err) { onRejectedNext(err) } } switch (_status) { case PENDING: this._fulfilledQueues.push(fulfilled) this._rejectedQueues.push(rejected) break case FULFILLED: fulfilled(_value) break case REJECTED: rejected(_value) break } }) }}
第四版 finally
class MyPromise { constructor(handle) { if (!isFunction(handle)) { throw new Error('MyPromise must accept a function as a parameter') } this._status = PENDING this._value = undefined this._fulfilledQueues = [] this._rejectedQueues = [] try { handle(this._resolve.bind(this), this._reject.bind(this)) } catch (err) { this._reject(err) } } _resolve(val) { const run = () => { if (this._status !== PENDING) return const runFulfilled = (value) => { let cb; while (cb = this._fulfilledQueues.shift()) { cb(value) } } const runRejected = (error) => { let cb; while (cb = this._rejectedQueues.shift()) { cb(error) } } if (val instanceof MyPromise) { val.then(value => { this._value = value this._status = FULFILLED runFulfilled(value) }, err => { this._value = err this._status = REJECTED runRejected(err) }) } else { this._value = val this._status = FULFILLED runFulfilled(val) } } setTimeout(run, 0) } _reject(err) { if (this._status !== PENDING) return const run = () => { this._status = REJECTED this._value = err let cb; while (cb = this._rejectedQueues.shift()) { cb(err) } } setTimeout(run, 0) } then(onFulfilled, onRejected) { const { _value, _status } = this return new MyPromise((onFulfilledNext, onRejectedNext) => { let fulfilled = value => { try { if (!isFunction(onFulfilled)) { onFulfilledNext(value) } else { let res = onFulfilled(value); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext) } else { onFulfilledNext(res) } } } catch (err) { onRejectedNext(err) } } let rejected = error => { try { if (!isFunction(onRejected)) { onRejectedNext(error) } else { let res = onRejected(error); if (res instanceof MyPromise) { res.then(onFulfilledNext, onRejectedNext) } else { onFulfilledNext(res) } } } catch (err) { onRejectedNext(err) } } switch (_status) { case PENDING: this._fulfilledQueues.push(fulfilled) this._rejectedQueues.push(rejected) break case FULFILLED: fulfilled(_value) break case REJECTED: rejected(_value) break } }) } // 增加catch办法 catch(onRejected) { return this.then(undefined, onRejected) } // 增加动态resolve办法 static resolve(value) { // 如果参数是MyPromise实例,间接返回这个实例 if (value instanceof MyPromise) return value return new MyPromise(resolve => resolve(value)) } // 增加动态reject办法 static reject(value) { return new MyPromise((resolve, reject) => reject(value)) } // 增加动态all办法 static all(list) { return new MyPromise((resolve, reject) => { /** * 返回值的汇合 */ let values = [] let count = 0 for (let [i, p] of list.entries()) { // 数组参数如果不是MyPromise实例,先调用MyPromise.resolve this.resolve(p).then(res => { values[i] = res count++ // 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled if (count === list.length) resolve(values) }, err => { // 有一个被rejected时返回的MyPromise状态就变成rejected reject(err) }) } }) } // 增加动态race办法 static race(list) { return new MyPromise((resolve, reject) => { for (let p of list) { // 只有有一个实例率先扭转状态,新的MyPromise的状态就跟着扭转 this.resolve(p).then(res => { resolve(res) }, err => { reject(err) }) } }) } finally(cb) { return this.then( value => MyPromise.resolve(cb()).then(() => value), reason => MyPromise.resolve(cb()).then(() => { throw reason }) ); }}
其它问题
1. Promise与宏观工作、async函数等执行程序问题
Promise是微工作的一种实现,给出如下的代码,剖析其输入程序
//题目1async function a1 () { console.log('a1 start') await a2() console.log('a1 end')}async function a2 () { console.log('a2')}console.log('script start')setTimeout(() => { console.log('setTimeout')}, 0)Promise.resolve().then(() => { console.log('promise1')})a1()let promise2 = new Promise((resolve) => { resolve('promise2.then') console.log('promise2')})promise2.then((res) => { console.log(res) Promise.resolve().then(() => { console.log('promise3') })})console.log('script end')//题目2async function async1() { console.log('async1 start'); await async2(); await async3() console.log('async1 end');}async function async2() { console.log('async2');}async function async3() { console.log('async3');}console.log('script start');setTimeout(function() { console.log('setTimeout');}, 0)async1();new Promise(function(resolve) { console.log('promise1'); resolve();}).then(function() { console.log('promise2');});console.log('script end');//题目3async function async1(){ console.log('async1 start') await async2() console.log('async1 end') }async function async2(){ console.log('async2')}console.log('script start')setTimeout(function(){ console.log('setTimeout0') },0) setTimeout(function(){ console.log('setTimeout3') },3) setImmediate(() => console.log('setImmediate'));process.nextTick(() => console.log('nextTick'));async1();new Promise(function(resolve){ console.log('promise1') resolve(); console.log('promise2')}).then(function(){ console.log('promise3')})console.log('script end')
答案:
题目一:script start->a1 start->a2->promise2->script end->promise1->a1 end->promise2.then->promise3->setTimeout
题目二:script start->async1 start->async2->promise1->script end->async3->promise2->async1 end->setTimeout
在浏览器console可试验
题目三:script start->async1 start->async2->promise1->promise2
->script end->nextTick->async1 end->promise3->setTimeout->setImmediate->setTimeout3
在node环境中可试验
2. 如何实现all、如何实现链式调用的
all()的实现:函数中保护了一个数组和计数器,数组的大小为初始时all函数中传递的Promise对象数量,数组存储各个Promise执行胜利后resolve失去的后果,每胜利一个计数器+1,直到计数器累加到数组大小时即调用resolve(value),只有有一个Promise执行到失败的回调,即全副失败。
链式调用的实现:then函数返回的仍然是一个Promise,见第二版的Promise实现。
3. all中任何一个Promise出错,导致其它正确的数据也无奈应用,如何解决呢?
办法1:应用allSettled代替;办法2:改写Promise,将reject操作换成是resolve(new Error("自定义的谬误"));办法3:引入第三方库promise-transaction
4.Promise真的好用吗,它存在什么问题?
问题1:没有提供中途勾销的机制;问题2:必须要设置回调,否则外部谬误无奈在内部反映进去;问题3:应用时仍然存在大量Promise的api,逻辑不清晰。