原文 : https://juejin.cn/post/7269640045043777576
首先,Promise
肯定是一个类,所以我们才可以new
它,然后Promise实例化
的时候给它传入一个回调我们叫它executor
方法,Promise 内部会立即调用
这个executor方法
,并且会传入resolve
和reject
两个函数作为调用参数,另外在 Promise 类的原型上应该提供一个then
方法,它里面可以传入两个回调,分别为Promise成功的回调
和Promise失败的回调
。调用resolve
后会走入成功的回调中
,调用reject
后会走入失败的回调中
。
初级版本 Promise 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 const PENDING = "PENDING" ;const FULFILLED = "FULFILLED" ;const REJECTED = "REJECTED" ;class Promise { constructor (executor ) { this .value = undefined ; this .reason = undefined ; this .status = PENDING ; this .onResolvedCallbacks = []; this .onRejectedCallbacks = []; const resolve = (value ) => { if (this .status === PENDING ) { this .value = value; this .status = FULFILLED ; this .onResolvedCallbacks .forEach ((fn ) => fn ()); } }; const reject = (reason ) => { if (this .status === PENDING ) { this .reason = reason; this .status = REJECTED ; this .onRejectedCallbacks .forEach ((fn ) => fn ()); } }; try { executor (resolve,reject) } catch (err) { reject (err); } } } then (onFulfilled, onRejected ) { if (this .status === FULFILLED ) { onFulfilled && onFulfilled (this .value ); } if (this .status === REJECTED ) { onRejected && onRejected (this .reason ); } if (this .status === PENDING ) { this .onResolvedCallbacks .push (() => { onFulfilled (this .value ); }); this .onRejectedCallbacks .push (() => { onRejected (this .reason ); }); } } }module .exports = Promise ;
链式调用 Promise
内部调用 then 方法时,返回了一个新的 promise,并让这个新的 promise 接管了它下一个 then 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 const PENDING = "PENDING" ;const FULFILLED = "FULFILLED" ;const REJECTED = "REJECTED" ;class Promise { constructor (executor ) { this .value = undefined ; this .reason = undefined ; this .status = PENDING ; this .onResolvedCallbacks = []; this .onRejectedCallbacks = []; const resolve = (value ) => { if (this .status === PENDING ) { this .value = value; this .status = FULFILLED ; this .onResolvedCallbacks .forEach ((fn ) => fn ()); } }; const reject = (reason ) => { if (this .status === PENDING ) { this .reason = reason; this .status = REJECTED ; this .onRejectedCallbacks .forEach ((fn ) => fn ()); } }; try { executor (resolve, reject); } catch (err) { reject (err); } } then (onFulfilled, onRejected ) { const promise2 = new Promise ((resolve, reject ) => { if (this .status === FULFILLED ) { const x = onFulfilled (this .value ); resolvePromise (promise2, x, resolve, reject); } if (this .status === REJECTED ) { const x = onRejected (this .reason ); resolvePromise (promise2, x, resolve, reject); } if (this .status === PENDING ) { this .onResolvedCallbacks .push (() => { const x = onFulfilled (this .value ); resolvePromise (promise2, x, resolve, reject); }); this .onRejectedCallbacks .push (() => { const x = onRejected (this .reason ); resolvePromise (promise2, x, resolve, reject); }); } }); return promise2; } }module .exports = Promise ;
这里最主要的就是resolvePromise
,来看下它做了什么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 function resolvePromise (promise2, x, resolve, reject ) { if (promise2 === x) { return reject ( new TypeError ( "UnhandledPromiseRejectionWarning: TypeError: Chaining cycle detected for promise #<Promise>" ) ); } let called; if ((typeof x === "object" && x !== null ) || typeof x === "function" ) { try { const then = x.then ; if (typeof then === "function" ) { then.call ( x, (y ) => { if (called) return ; called = true ; resolvePromise (promise2, y, resolve, reject); }, (r ) => { if (called) return ; called = true ; reject (r); } ); } else { resolve (x); } } catch (e) { if (called) return ; called = true ; reject (e); } } else { resolve (x); } }
因为promise
在EventLoop
里面是个微任务,不过我们可以简单通过setTimout
模拟。
然后我们再加上一些报错的捕获代码以及一些参数的兼容代码,以及实现catch
方法。
完全版本 Promise 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 const PENDING = "PENDING" ;const FULFILLED = "FULFILLED" ;const REJECTED = "REJECTED" ;class Promise { constructor (executor ) { this .value = undefined ; this .reason = undefined ; this .status = PENDING ; this .onResolvedCallbacks = []; this .onRejectedCallbacks = []; const resolve = (value ) => { if (this .status === PENDING ) { this .value = value; this .status = FULFILLED ; this .onResolvedCallbacks .forEach ((fn ) => fn ()); } }; const reject = (reason ) => { if (this .status === PENDING ) { this .reason = reason; this .status = REJECTED ; this .onRejectedCallbacks .forEach ((fn ) => fn ()); } }; try { executor (resolve, reject); } catch (e) { reject (e); } } then (onFulfilled, onRejected ) { onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v ) => v; onRejected = typeof onRejected === "function" ? onRejected : (err ) => { throw err; }; const promise2 = new Promise ((resolve, reject ) => { if (this .status === FULFILLED ) { setTimeout (() => { try { const x = onFulfilled (this .value ); resolvePromise (promise2, x, resolve, reject); } catch (e) { reject (e); } }, 0 ); } if (this .status === REJECTED ) { setTimeout (() => { try { const x = onRejected (this .reason ); resolvePromise (promise2, x, resolve, reject); } catch (e) { reject (e); } }, 0 ); } if (this .status === PENDING ) { this .onResolvedCallbacks .push (() => { setTimeout (() => { try { const x = onFulfilled (this .value ); resolvePromise (promise2, x, resolve, reject); } catch (e) { reject (e); } }, 0 ); }); this .onRejectedCallbacks .push (() => { setTimeout (() => { try { const x = onRejected (this .reason ); resolvePromise (promise2, x, resolve, reject); } catch (e) { reject (e); } }, 0 ); }); } }); return promise2; } catch (errCallback) { return this .then (null , errCallback); } }function resolvePromise (promise2, x, resolve, reject ) { if (promise2 === x) { return reject ( new TypeError ( "UnhandledPromiseRejectionWarning: TypeError: Chaining cycle detected for promise #<Promise>" ) ); } let called; if ((typeof x === "object" && x !== null ) || typeof x === "function" ) { try { const then = x.then ; if (typeof then === "function" ) { then.call ( x, (y ) => { if (called) return ; called = true ; resolvePromise (promise2, y, resolve, reject); }, (r ) => { if (called) return ; called = true ; reject (r); } ); } else { resolve (x); } } catch (e) { if (called) return ; called = true ; reject (e); } } else { resolve (x); } }module .exports = Promise ;
测试 promise 是否符合规范
首先安装依赖包 npm i promises-aplus-tests -D
在我们的代码中添加
1 2 3 4 5 6 7 8 Promise .defer = Promise .deferred = function ( ){ let dfd = {}; dfd.promise = new Promise ((resolve, reject )=> { dfd.resolve = resolve; dfd.reject = reject; }); return dfd; }
注意别忘了 module.exports = Promise;
运行 promises-aplus-tests 文件名
即可
有 872 个测试用例,全部通过即可以认为这是一个标准的 promise。
实现Promise的各种方法
Promise 的实例方法有 then/catch/finally 三种,静态方法有 all/race/allSettled/any/resolve/reject 六种
then 和 catch 上面已经实现过了
实现 Promise.resolve 实现 resolve 静态方法有三个要点:
传参为一个 Promise, 则直接返回它。
传参为一个 thenable 对象,返回的 Promise 会跟随这个对象,采用它的最终状态作为自己的状态。
其他情况,直接返回以该值为成功状态的promise对象。
1 2 3 4 5 6 7 8 9 10 11 Promise .resolve = (param ) => { if (param instanceof Promise ) return param; return new Promise ((resolve, reject ) => { if (param && param.then && typeof param.then === 'function' ) { param.then (resolve, reject); }else { resolve (param); } }) }
实现 Promise.reject Promise.reject 中传入的参数会作为一个 reason 原封不动地往下传, 实现如下:
1 2 3 4 5 Promise .reject = function (reason ) { return new Promise ((resolve, reject ) => { reject (reason); }); }
实现 Promise.prototype.finally 无论当前 Promise 是成功还是失败,调用finally之后都会执行 finally 中传入的函数,并且将值原封不动的往下传。
1 2 3 4 5 6 7 8 9 10 11 Promise .prototype .finally = function (callback ) { this .then (value => { return Promise .resolve (callback ()).then (() => { return value; }) }, error => { return Promise .resolve (callback ()).then (() => { throw error; }) }) }
实现 Promise.all 对于 all
方法而言,需要完成下面的核心功能:
传入参数为一个空的可迭代对象,则直接进行resolve
。
如果参数中有一个promise
失败,那么Promise.all
返回的promise
对象失败。
在任何情况下,Promise.all
返回的 promise
的完成状态的结果都是一个数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Promise .all = function (promises ) { return new Promise ((resolve, reject ) => { let result = []; let index = 0 ; let len = promises.length ; if (len === 0 ) { resolve (result); return ; } for (let i = 0 ; i < len; i++) { Promise .resolve (promise[i]).then (data => { result[i] = data; index++; if (index === len) resolve (result); }).catch (err => { reject (err); }) } }) }
实现 Promise.allSettled Promise.allSettled() 方法只有等到参数数组的所有 Promise 实例都发生状态变更,返回的 Promise 实例才会发生状态变更,无论是执行 resolve 回调还是 reject 回调的状态。 打个比方:多名员工分别同时进行多个项目,你要求每个一个项目都必须完成,然后得到所有项目是令你满意还是令你不满意的。强调的是最终结果。
同时因为 Promise.allSettled() 和 Promise.all() 都是对所有 Promise 实例的一种处理,下面就可以利用 Promise.all() 来实现 Promise.allSettled() 方法。
1 2 3 4 5 6 7 8 9 10 MyPromise .allSettled = function (promises ) { return Promise .all ( promises.map ((item ) => Promise .resolve (item).then ( (value ) => ({ status : 'fulfilled' , value }), (reason ) => ({ status : 'rejected' , reason }) ) ) ); };
实现 Promise.race race
的实现相比之下就简单一些,只要有一个 promise
执行完,直接 resolve
并停止执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Promise .race = function (promises ) { return new Promise ((resolve, reject ) => { if (promises.length === 0 ) resolve (); else { let len = promises.length ; if (len === 0 ) return ; for (let i = 0 ; i < len; i++) { Promise .resolve (promise[i]).then (data => { resolve (data); return ; }).catch (err => { reject (err); return ; }) } } }) }
实现 Promise.any Promise.any() 方法是返回任意一个最快执行 resolve 回调的 Promise 实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 MyPromise .any = function (promises ) { return new Promise ((resolve, reject ) => { if (promises.length === 0 ) { return resolve (); } else { let result = []; let index = 0 ; for (let i = 0 ; i < promises.length ; i++) { promises[i].then ( (value ) => { return resolve (value); }, (reason ) => { result[i] = reason; if (++index === promises.length ) { return reject (new AggregateError (result)); } } ); } } }); };
over