Build your own Promise

Tags
Web Dev
Published
November 30, 2020
Author
HJS
function Promise(executor) { this.resolveCallbacks = []; this.rejectCallbacks = []; this.status = 'pending' /** * 1. resolve和reject前判定status为pending,避免多次reslove或reject * 2. resolve、reject为箭头函数,捕获第一次this后(即new绑定到Promise对象)不受this机制影响 * 3. 当前Promise对象何时执行resolve(handleResolve)依赖x,使用promiseResolutionProcedure处理 * 4. 规范中reject无需像resolve特殊处理,无论reason是什么类型,直接reject即可 */ const resolve = (x) => { if (this.status !== 'pending') return const handleResolve = (value) => { this.value = value this.status = 'fulfilled' this.resolveCallbacks.forEach((callback) => callback(value)); } promiseResolutionProcedure(this, x, handleResolve, reject) } const reject = (reason) => { if (this.status !== 'pending') return this.reason = reason this.status = 'rejected' this.rejectCallbacks.forEach((callback) => callback(reason)); } try { executor(resolve, reject); } catch (error) { reject(error) } } /** * A:调用then的Promise对象 * B:then返回的Promise对象 * C:callback的返回结果x * * then的作用可以总结为两点: * 1. 接收onFulfilled、onRejected回调,将回调和B的resolve、reject逻辑包装成一个函数,该函数决定了B的状态。在此之前根据A的状态 决定立即执行该函数,或保存到A的数组里。 * 2. 返回一个Promise对象,使得能够链式调用 */ Promise.prototype.then = function (onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }; const promise = new Promise((resolve, reject) => { /** * handleNext将回调和B的resolve、reject逻辑包装成一个函数,该函数决定了B的状态 * 1. Promise.then的onFulfilled、onRejected参数是异步调用的,这里用setTimeout模拟 */ const handleNext = (callback) => (value) => { setTimeout(() => { try { const x = callback(value); promiseResolutionProcedure(promise, x, resolve, reject) } catch (error) { reject(error) } }); } const handleResolve = handleNext(onFulfilled) const handleReject = handleNext(onRejected) /** * 1. this指向A * 2. 如果Promise状态为fulfilled,就直接执行handleResolve * 如果Promise状态为rejected,就直接执行handleReject * 如果Promise状态为pending,将handleResolve和handleReject存入数组,等Promise对象resolve或reject后遍历数组调用 */ if (this.status === 'fulfilled') { handleResolve(this.value); } else if (this.status === 'rejected') { handleReject(this.reason) } else if (this.status === 'pending') { this.resolveCallbacks.push(handleResolve); this.rejectCallbacks.push(handleReject); } }); return promise } /** * 1.处理thenable对象,同时也将Promise对象当作thenable对象处理,当然也可以选择单独抽出来一个分支处理 * 2.handleUnique使得promiseResolutionProcedure、reject共享一个标识符,同时执行或重复执行时会被忽略 * 3.注意then.call那一句,后面(y)...和(r)...是executor的resolve和reject的,别被搞乱了 */ const promiseResolutionProcedure = function (promise, x, resolve, reject) { const [handleResolution, handleReject] = handleUnique(promiseResolutionProcedure, reject) if (x === promise) reject(new TypeError('Chaining cycle detected for promise')) else if (isObjectOrFunction(x)) { try { let then = x.then; if (typeof then === 'function') { then.call(x, (y) => handleResolution(promise, y, resolve, reject), (r) => handleReject(r)); } else resolve(x); } catch (e) { handleReject(e); } } else resolve(x) } const isObjectOrFunction = function (x) { return x !== null && typeof x === 'object' || typeof x === 'function' } /** * 接收函数数组funcs,共享一个标识符flag */ const handleUnique = function (...funcs) { let flag = false return funcs.map((func) => { return (...args) => { if (flag) return flag = true func(...args) } }) } module.exports = Promise