作者声明

本人将迁移至个人公众号「前端Q」及「掘金」平台写文章。博客园的文章将不再及时更新发布。欢迎大家关注公众号「前端Q」及我的掘金主页
<https://juejin.im/user/5874526761ff4b006d4fd9a4/posts>:
https://juejin.im/user/5874526761ff4b006d4fd9a4/posts
<https://juejin.im/user/5874526761ff4b006d4fd9a4/posts>

 

Promise 必须为以下三种状态之一:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。一旦Promise 被
resolve 或 reject,不能再迁移至其他任何状态(即状态 immutable)。

基本过程:

* 初始化 Promise 状态(pending)
* 执行 then(..) 注册回调处理数组(then 方法可被同一个 promise 调用多次)
* 立即执行 Promise 中传入的 fn 函数,将Promise 内部 resolve、reject 函数作为参数传递给 fn ,按事件机制时机处理
* Promise里的关键是要保证,then方法传入的参数 onFulfilled 和
onRejected,必须在then方法被调用的那一轮事件循环之后的新执行栈中执行。
真正的链式Promise是指在当前promise达到fulfilled状态后,即开始进行下一个promise.

链式调用

先从 Promise 执行结果看一下,有如下一段代码:
new Promise((resolve, reject) => { setTimeout(() => { resolve({ test: 1 })
resolve({ test:2 }) reject({ test: 2 }) }, 1000) }).then((data) => {
console.log('result1', data) },(data1)=>{ console.log('result2',data1)
}).then((data)=> { console.log('result3', data) }) //result1 { test: 1 } //
result3 undefined
显然这里输出了不同的 data。由此可以看出几点:

* 可进行链式调用,且每次 then 返回了新的 Promise(2次打印结果不一致,如果是同一个实例,打印结果应该一致。
* 只输出第一次 resolve 的内容,reject 的内容没有输出,即 Promise 是有状态且状态只可以由pending ->
fulfilled或 pending-> rejected,是不可逆的。
* then 中返回了新的 Promise,但是then中注册的回调仍然是属于上一个 Promise 的。
基于以上几点,我们先写个基于 PromiseA+
<https://link.juejin.im?target=https%3A%2F%2Fpromisesaplus.com%2F> 规范的只含
resolve 方法的 Promise 模型:
function Promise(fn){ let state = 'pending'; let value = null; const callbacks
= []; this.then = function (onFulfilled){ return new Promise((resolve, reject)=>
{ handle({//桥梁,将新 Promise 的 resolve 方法,放到前一个 promise 的回调对象中 onFulfilled,
resolve }) }) }function handle(callback){ if(state === 'pending'){
callbacks.push(callback)return; } if(state === 'fulfilled'){ if(!
callback.onFulfilled){ callback.resolve(value)return; } const ret =
callback.onFulfilled(value)//处理回调 callback.resolve(ret) //处理下一个 promise 的resolve
} }function resolve(newValue){ const fn = ()=>{ if(state !== 'pending')return
state= 'fulfilled'; value = newValue handelCb() } setTimeout(fn,0) //基于
PromiseA+ 规范 } function handelCb(){ while(callbacks.length) { const fulfiledFn
= callbacks.shift(); handle(fulfiledFn); }; } fn(resolve) }
这个模型简单易懂,这里最关键的点就是在 then 中新创建的 Promise,它的状态变为 fulfilled 的节点是在上一个
Promise的回调执行完毕的时候。也就是说当一个 Promise 的状态被 fulfilled 之后,会执行其回调函数,而回调函数返回的结果会被当作
value,返回给下一个 Promise(也就是then 中产生的 Promise),同时下一个 Promise的状态也会被改变(执行 resolve 或
reject),然后再去执行其回调,以此类推下去...链式调用的效应就出来了。

但是如果仅仅是例子中的情况,我们可以这样写:
new Promise((resolve, reject) => { setTimeout(() => { resolve({ test: 1 }) },
1000) }).then((data) => { console.log('result1', data) //dosomething
console.log('result3') }) //result1 { test: 1 } //result3
实际上,我们常用的链式调用,是用在异步回调中,以解决"回调地狱"的问题。如下例子:
new Promise((resolve, reject) => { setTimeout(() => { resolve({ test: 1 }) },
1000) }).then((data) => { console.log('result1', data) //dosomething return
test() }).then((data)=> { console.log('result2', data) }) function test(id) {
return new Promise(((resolve) => { setTimeout(() => { resolve({ test: 2 }) },
5000) })) } //基于第一个 Promise 模型,执行后的输出 //result1 { test: 1 } //result2 Promise
{then: ƒ}
用上面的 Promise 模型,得到的结果显然不是我们想要的。认真看上面的模型,执行 callback.resolve 时,传入的参数是
callback.onFulfilled 执行完成的返回,显然这个测试例子返回的就是一个 Promise,而我们的 Promise 模型中的 resolve
方法并没有特殊处理。那么我们将 resolve 改一下:
function Promise(fn){ ... function resolve(newValue){ const fn = ()=>{ if
(state !== 'pending')return if(newValue && (typeof newValue === 'object' ||
typeof newValue === 'function')){ const {then} = newValue if(typeof then ===
'function'){ // newValue 为新产生的 Promise,此时resolve为上个 promise 的resolve //
相当于调用了新产生 Promise 的then方法,注入了上个 promise 的resolve 为其回调
then.call(newValue,resolve)return } } state = 'fulfilled'; value = newValue
handelCb() } setTimeout(fn,0) } ... }
用这个模型,再测试我们的例子,就得到了正确的结果:
new Promise((resolve, reject) => { setTimeout(() => { resolve({ test: 1 }) },
1000) }).then((data) => { console.log('result1', data) //dosomething return
test() }).then((data)=> { console.log('result2', data) }) function test(id) {
return new Promise(((resolve, reject) => { setTimeout(() => { resolve({ test: 2
}) },5000) })) } //result1 { test: 1 } //result2 { test: 2 }
显然,新增的逻辑就是针对 resolve 入参为 Promise 的时候的处理。我们观察一下 test 里面创建的 Promise,它是没有调用
then方法的。从上面的分析我们已经知道 Promise 的回调函数就是通过调用其 then 方法注册的,因此 test 里面创建的 Promise
其回调函数为空。

显然如果没有回调函数,执行 resolve 的时候,是没办法链式下去的。因此,我们需要主动为其注入回调函数。

我们只要把第一个 then 中产生的 Promise 的 resolve 函数的执行,延迟到 test 里面的 Promise 的状态为
onFulfilled 的时候再执行,那么链式就可以继续了。所以,当 resolve 入参为 Promise 的时候,调用其 then
方法为其注入回调函数,而注入的是前一个 Promise 的 resolve 方法,所以要用 call 来绑定 this 的指向。

基于新的 Promise 模型,上面的执行过程产生的 Promise 实例及其回调函数,可以用看下表:

Promisecallback
P1 [{onFulfilled:c1(第一个then中的fn),resolve:p2resolve}]
P2 (P1 调用 then 时产生) [{onFulfilled:c2(第二个then中的fn),resolve:p3resolve}]
P3 (P2 调用 then 时产生) []
P4 (执行c1中产生[调用 test ]) [{onFulfilled:p2resolve,resolve:p5resolve}]
P5 (调用p2resolve 时,进入 then.call 逻辑中产生) []
有了这个表格,我们就可以清晰知道各个实例中 callback 执行的顺序是:

c1 -> p2resolve -> c2 -> p3resolve -> [] -> p5resolve -> []

以上就是链式调用的原理了。

reject

下面我们再来补全 reject 的逻辑。只需要在注册回调、状态改变时加上 reject 的逻辑即可。

完整代码如下:
function Promise(fn){ let state = 'pending'; let value = null; const callbacks
= []; this.then = function (onFulfilled,onRejected){ return new
Promise((resolve, reject)=>{ handle({ onFulfilled, onRejected, resolve, reject
}) }) }function handle(callback){ if(state === 'pending'){
callbacks.push(callback)return; } const cb = state === 'fulfilled' ?
callback.onFulfilled:callback.onRejected; const next= state === 'fulfilled'?
callback.resolve:callback.reject;if(!cb){ next(value) return; } const ret =
cb(value) next(ret) }function resolve(newValue){ const fn = ()=>{ if(state !==
'pending')return if(newValue && (typeof newValue === 'object' || typeof
newValue === 'function')){ const {then} = newValue if(typeof then === 'function'
){// newValue 为新产生的 Promise,此时resolve为上个 promise 的resolve //相当于调用了新产生 Promise
的then方法,注入了上个 promise 的resolve 为其回调 then.call(newValue,resolve, reject) return
} } state= 'fulfilled'; value = newValue handelCb() } setTimeout(fn,0) }
function reject(error){ const fn = ()=>{ if(state !== 'pending')return if(error
&& (typeof error === 'object' || typeof error === 'function')){ const {then} =
errorif(typeof then === 'function'){ then.call(error,resolve, reject) return }
} state= 'rejected'; value = error handelCb() } setTimeout(fn,0) } function
handelCb(){while(callbacks.length) { const fn = callbacks.shift(); handle(fn);
}; } fn(resolve, reject) }
异常处理

异常通常是指在执行成功/失败回调时代码出错产生的错误,对于这类异常,我们使用 try-catch 来捕获错误,并将 Promise 设为 rejected
状态即可。

handle代码改造如下:
function handle(callback){ if(state === 'pending'){ callbacks.push(callback)
return; } const cb = state === 'fulfilled' ?
callback.onFulfilled:callback.onRejected; const next= state === 'fulfilled'?
callback.resolve:callback.reject;if(!cb){ next(value) return; } try { const ret
= cb(value) next(ret) } catch (e) { callback.reject(e); } }
我们实际使用时,常习惯注册 catch 方法来处理错误,例:
new Promise((resolve, reject) => { setTimeout(() => { resolve({ test: 1 }) },
1000) }).then((data) => { console.log('result1', data) //dosomething return
test() }).catch((ex) => { console.log('error', ex) })
实际上,错误也好,异常也罢,最终都是通过reject实现的。也就是说可以通过 then 中的错误回调来处理。所以我们可以增加这样的一个 catch 方法:
function Promise(fn){ ... this.then = function (onFulfilled,onRejected){ return
new Promise((resolve, reject)=>{ handle({ onFulfilled, onRejected, resolve,
reject }) }) }this.catch = function (onError){ this.then(null,onError) } ... }
Finally方法

在实际应用的时候,我们很容易会碰到这样的场景,不管Promise最后的状态如何,都要执行一些最后的操作。我们把这些操作放到 finally 中,也就是说
finally 注册的函数是与 Promise 的状态无关的,不依赖 Promise 的执行结果。所以我们可以这样写 finally 的逻辑:
function Promise(fn){ ... this.catch = function (onError){ this.then(null
,onError) }this.finally = function (onDone){ this.then(onDone,onError) } ... }
resolve 方法和 reject 方法

实际应用中,我们可以使用 Promise.resolve 和 Promise.reject 方法,用于将于将非 Promise 实例包装为 Promise
实例。如下例子:
Promise.resolve({name:'winty'}) Promise.reject({name:'winty'}) // 等价于 new
Promise(resolve => resolve({name:'winty'})) new Promise((resolve,reject) =>
reject({name:'winty'}))
这些情况下,Promise.resolve 的入参可能有以下几种情况:

* 无参数 [直接返回一个resolved状态的 Promise 对象]
* 普通数据对象 [直接返回一个resolved状态的 Promise 对象]
* 一个Promise实例 [直接返回当前实例]
* 一个thenable对象(thenable对象指的是具有then方法的对象) [转为 Promise
对象,并立即执行thenable对象的then方法。]
基于以上几点,我们可以实现一个 Promise.resolve 方法如下:
function Promise(fn){ ... this.resolve = function (value){ if (value && value
instanceof Promise) { return value; } else if (value && typeof value ===
'object' &&typeof value.then === 'function'){ let then = value.then; return new
Promise(resolve => { then(resolve); }); } else if (value) { return new
Promise(resolve => resolve(value)); } else { return new Promise(resolve =>
resolve()); } } ... }

Promise.reject与Promise.resolve类似,区别在于Promise.reject始终返回一个状态的rejected的Promise实例,而Promise.resolve的参数如果是一个Promise实例的话,返回的是参数对应的Promise实例,所以状态不一
定。 因此,reject 的实现就简单多了,如下:
function Promise(fn){ ... this.reject = function (value){ return new Promise(
function(resolve, reject) { reject(value); }); } ... }
Promise.all

入参是一个 Promise 的实例数组,然后注册一个 then 方法,然后是数组中的 Promise 实例的状态都转为 fulfilled 之后则执行
then 方法。这里主要就是一个计数逻辑,每当一个 Promise 的状态变为 fulfilled 之后就保存该实例返回的数据,然后将计数减一,当计数器变为
0 时,代表数组中所有 Promise 实例都执行完毕。
function Promise(fn){ ... this.all = function (arr){ var args =
Array.prototype.slice.call(arr);return new Promise(function(resolve, reject) {
if(args.length === 0) return resolve([]); var remaining = args.length; function
res(i, val) {try { if(val && (typeof val === 'object' || typeof val ===
'function')) { var then = val.then; if(typeof then === 'function') {
then.call(val,function(val) { res(i, val); }, reject); return; } } args[i] =
val;if(--remaining === 0) { resolve(args); } } catch(ex) { reject(ex); } } for(
var i = 0; i < args.length; i++) { res(i, args[i]); } }); } ... }
Promise.race

有了 Promise.all 的理解,Promise.race 理解起来就更容易了。它的入参也是一个 Promise 实例数组,然后其 then
注册的回调方法是数组中的某一个 Promise 的状态变为 fulfilled 的时候就执行。因为 Promise 的状态只能改变一次,那么我们只需要把
Promise.race 中产生的 Promise 对象的 resolve 方法,注入到数组中的每一个 Promise 实例中的回调函数中即可。
function Promise(fn){ ... this.race = function(values) { return new Promise(
function(resolve, reject) { for(var i = 0, len = values.length; i < len; i++) {
values[i].then(resolve, reject); } }); } ... }
总结

Promise 源码不过几百行,我们可以从执行结果出发,分析每一步的执行过程,然后思考其作用即可。其中最关键的点就是要理解 then
函数是负责注册回调的,真正的执行是在 Promise 的状态被改变之后。而当 resolve 的入参是一个 Promise 时,要想链式调用起来,就必须调用其
then 方法(then.call),将上一个 Promise 的 resolve 方法注入其回调数组中。

参考资料

* PromiseA+规范
<https://link.juejin.im?target=https%3A%2F%2Fpromisesaplus.com%2F>
* Promise 实现原理精解
<https://link.juejin.im?target=https%3A%2F%2Fzhuanlan.zhihu.com%2Fp%2F58428287>
* 30分钟,让你彻底明白Promise原理
<https://link.juejin.im?target=https%3A%2F%2Fmengera88.github.io%2F2017%2F05%2F18%2FPromise%25E5%258E%259F%25E7%2590%2586%25E8%25A7%25A3%25E6%259E%2590%2F>
完整 Promise 模型
function Promise(fn) { let state = 'pending' let value = null const callbacks =
[]this.then = function (onFulfilled, onRejected) { return new
Promise((resolve, reject) => { handle({ onFulfilled, onRejected, resolve,
reject, }) }) }this.catch = function (onError) { this.then(null, onError) } this
.finally = function (onDone) { this.then(onDone, onError) } this.resolve =
function (value) { if (value && value instanceof Promise) { return value } if
(value &&typeof value === 'object' && typeof value.then === 'function') { const
{ then }= value return new Promise((resolve) => { then(resolve) }) } if (value)
{return new Promise(resolve => resolve(value)) } return new Promise(resolve =>
resolve()) }this.reject = function (value) { return new Promise(((resolve,
reject) => { reject(value) })) } this.all = function (arr) { const args =
Array.prototype.slice.call(arr)return new Promise(((resolve, reject) => { if
(args.length === 0)return resolve([]) let remaining = args.length function
res(i, val) {try { if (val && (typeof val === 'object' || typeof val ===
'function')) { const { then } = val if (typeof then === 'function') {
then.call(val, (val)=> { res(i, val) }, reject) return } } args[i] = val if
(--remaining === 0) { resolve(args) } } catch (ex) { reject(ex) } } for (let i
= 0; i < args.length; i++) { res(i, args[i]) } })) } this.race = function
(values) {return new Promise(((resolve, reject) => { for (let i = 0, len =
values.length; i < len; i++) { values[i].then(resolve, reject) } })) } function
handle(callback) {if (state === 'pending') { callbacks.push(callback) return }
const cb= state === 'fulfilled' ? callback.onFulfilled : callback.onRejected
const next= state === 'fulfilled' ? callback.resolve : callback.reject if (!cb)
{ next(value)return } try { const ret = cb(value) next(ret) } catch (e) {
callback.reject(e) } }function resolve(newValue) { const fn = () => { if (state
!== 'pending')return if (newValue && (typeof newValue === 'object' || typeof
newValue === 'function')) { const { then } = newValue if (typeof then ===
'function') { // newValue 为新产生的 Promise,此时resolve为上个 promise 的resolve //
相当于调用了新产生 Promise 的then方法,注入了上个 promise 的resolve 为其回调 then.call(newValue,
resolve, reject)return } } state = 'fulfilled' value = newValue handelCb() }
setTimeout(fn,0) } function reject(error) { const fn = () => { if (state !==
'pending')return if (error && (typeof error === 'object' || typeof error ===
'function')) { const { then } = error if (typeof then === 'function') {
then.call(error, resolve, reject)return } } state = 'rejected' value = error
handelCb() } setTimeout(fn,0) } function handelCb() { while (callbacks.length)
{ const fn= callbacks.shift() handle(fn) } } fn(resolve, reject) }
最后

觉得内容有帮助可以关注下我的掘金主页 <https://juejin.im/user/5874526761ff4b006d4fd9a4/posts>:
https://juejin.im/user/5874526761ff4b006d4fd9a4/posts
<https://juejin.im/user/5874526761ff4b006d4fd9a4/posts>

觉得内容有帮助可以关注下我的公众号 「前端Q」,一起学习成长~~




友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信