文章首发于我的博客 https://github.com/mcuking/bl...实现源码请查阅 https://github.com/mcuking/bl...
本文次要是论述如何一步步实现一个合乎 Promises/A+ 标准 的 Promise。
Promise 实现
在实现之前,让咱们先看一段应用 Promise 的示例代码:
new Promise((resolve, reject) => { setTimeout(resolve('hello world'), 1000);}).then( (msg) => console.log(msg), (err) => console.error(err));
Promise 实例化对象过程的实现
首先咱们来先实现前半部分的性能,即:
new Promise((resolve, reject) => { setTimeout(resolve('hello world'), 1000);})
依据 Promises/A+ 标准 咱们能够合成一下须要做的事件:
- 首先采纳一个类来实现 Promise;
- 这个类的实例有一个状态属性,来示意 Promise 对象实例的三种状态:pending、resolved、rejected;
- Promise 实例化对象的时候会接管一个函数,会在实例化的时候就立刻执行;
- 上一点的立刻执行函数有两个参数:resolve 办法和 reject 办法。这两个办法次要是用来扭转 Promise 对象实例的状态,以及执行胜利/失败回调函数,这个逻辑放到前面来实现。
因而能够初步写出如下代码:
class Promise { constructor(executor) { if (typeof executor !== 'function') { throw new TypeError(`Promise resolver ${executor} is not a function`); } this.value = undefined; // Promise的值 this.status = 'pending'; // Promise以后的状态 const resolve = value => { // 胜利后的一系列操作(状态的扭转、胜利回调执行) }; const reject = reason => { // 失败后的一系列操作(状态的扭转、失败回调执行) }; try { // 思考到执行executor的过程中有可能出错,所以咱们用try/catch块给包起来,并且在出错后以catch到的值reject掉这个Promise executor(resolve, reject); // 执行executor } catch (e) { reject(e); } }}
接下来咱们来剖析如何实现第二局部--then 办法:
new Promise((resolve, reject) => { setTimeout(resolve('hello world'), 1000);}).then( (msg) => console.log(msg), (err) => console.error(err));
依据 Promises/A+ 标准 ,then 办法会接管两个参数,别离是胜利和失败的回调函数。当调用 resolve 办法将 Promise 状态从 pending 改为 resolved,并执行传入的胜利回调函数;调用 reject 办法将 Promise 状态从 pending 改为 rejected,并执行失败回调函数。
须要留神的是,一个 Promise 实例对象的 then 办法会被调用屡次,也就说 then 办法能够接管多个胜利/失败回调函数,所以须要应用数组来接管这些回调函数。当 Promise 实例对象的状态从 pending 变成 resovled/rejected 时,就遍历存储回调函数的数组执行所有的胜利/失败回调函数。
上面是对应的实现代码:
class Promise { constructor(executor) { if (typeof executor !== 'function') { throw new TypeError(`Promise resolver ${executor} is not a function`); } this.value = undefined; // Promise的值 this.status = 'pending'; // Promise以后的状态 this.onResolvedCallbacks = []; // Promise resolve时的回调函数集,因为在Promise完结之前有可能有多个回调增加到它下面 this.onRejectedCallbacks = []; // Promise reject时的回调函数集,因为在Promise完结之前有可能有多个回调增加到它下面 const resolve = value => { // 以后状态为pending时才会执行 if (this.status === 'pending') { this.status = 'resolved'; this.value = value; for (let i = 0; i < this.onResolvedCallbacks.length; i++) { this.onResolvedCallbacks[i](value); } } }; const reject = reason => { // 以后状态为pending时才会执行 if (this.status === 'pending') { this.status = 'rejected'; this.value = reason; for (let i = 0; i < this.onRejectedCallbacks.length; i++) { this.onRejectedCallbacks[i](reason); } } }; try { // 思考到执行executor的过程中有可能出错,所以咱们用try/catch块给包起来,并且在出错后以catch到的值reject掉这个Promise executor(resolve, reject); // 执行executor } catch (e) { reject(e); } }}
Promise 对象实例的 then 办法的实现
从下面的实现中,读者可能会有一个疑难:执行 resolve 和 reject 办法时,为什么会须要先判断以后 Promise 实例对象状态是否为 pending。如果是 pending,才会遍历执行回调函数数组中的回调函数呢?
因为当 Promise 实例对象曾经处于 resolved 或 rejected 状态时,传入的对应回调函数时就须要被立刻执行,并且只会被执行一次。再次调用 resolve 或 reject 办法不会再做任何操作。
例如当 Promise 实例曾经处于 resolved 状态时,调用 then 办法接管胜利回调函数时,该函数会被立刻执行。同理处于 rejected 状态时,会立刻执行 then 办法接管的失败回调函数。
另外须要明确的一点是,依据 Promises/A+ 标准,then 办法必须返回一个 Promise 对象,因而执行完 then 办法后,须要再返回一个新的 Promise 实例。
那么咱们就来依据方才的剖析来实现一下 then 办法。上面对应的实现代码:
// 减少then办法class Promise { constructor(executor) { // 代码同上 } // then办法接管两个参数,onResolved,onRejected,别离为Promise胜利或失败后的回调 then(onResolved, onRejected) { let self = this; let promise2; // 依据规范,如果then的参数不是function,则咱们须要疏忽它,此处以如下形式解决 onResolved = typeof onResolved === 'function' ? onResolved : function(v) {}; onRejected = typeof onRejected === 'function' ? onRejected : function(r) {}; if (self.status === 'resolved') { return (promise2 = new Promise(function(resolve, reject) { onResolved(self.data); })); } if (self.status === 'rejected') { return (promise2 = new Promise(function(resolve, reject) { onRejected(self.data); })); } if (self.status === 'pending') { return (promise2 = new Promise(function(resolve, reject) { self.onResolvedCallbacks.push(function(value) { onResolved(value); }); self.onRejectedCallbacks.push(function(reason) { onRejected(reason); }); })); } }}
- 以后 promise1 处于 pending 状态时,执行 then 办法时返回一个 promise2 的同时,在 promise2 对应的立刻执行函数中将接管到的回调函数塞入 promise1 的回调队列中;
- 当处于 resolved 时,则在返回的 promise2 对应的立刻执行函数中调用传入的胜利回调函数;
- 当处于 rejeted 时,则在返回的 promise2 对应的立刻执行函数中调用传入的失败回调函数。
这里读者可能会有一个疑难,为什么须要在 promise2 对应的立刻执行函数中执行塞入回调函数到队列或立刻执行回调函数的逻辑?间接在里面执行这些逻辑不就能够了么?这就波及到了下个要实现的性能了--Promise 链式调用
Promise 链式调用-回调函数返回值问题
依据 Promises/A+ 标准 :
promise2 = promise1.then(onFulfilled, onRejected);
如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行上面的 Promise 解决过程:[[Resolve]](promise2, x)
。具体规定如下:
这里咱们只思考 x 为 Promise 或非对象和函数的值的状况:
如果 onResolved/onRejected 的返回值 x 是一个 Promise 对象,间接取它的后果做为 promise2 的后果,即 x 接管了 promise2 的状态:如果 x 处于 pending 状态, promise2 需放弃为 pending 状态直至 x 被 resolve/reject 掉,如果 x 处于 resolved 状态,用雷同的 value 执行 promise2,如果 x 处于 rejected 状态,用雷同的 reason 回绝 promise2。
如果 x 不为对象或者函数,则以 x 为参数执行 promise2。
上面是对应的实现代码:
// 针对上一个Promise为pending时,上一个then返回值进行优化class Promise { constructor(executor) { // 同上 } // then办法接管两个参数,onResolved,onRejected,别离为Promise胜利或失败后的回调 then(onResolved, onRejected) { let self = this; let promise2; // 依据规范,如果then的参数不是function,则咱们须要疏忽它,此处以如下形式解决 onResolved = typeof onResolved === 'function' ? onResolved : function(v) {}; onRejected = typeof onRejected === 'function' ? onRejected : function(r) {}; if (self.status === 'resolved') { // 如果promise1(此处即为this/self)的状态曾经确定并且是resolved,咱们调用onResolved // 因为思考到有可能throw,所以咱们将其包在try/catch块里 return (promise2 = new Promise(function(resolve, reject) { try { let x = onResolved(self.value); if (x instanceof Promise) { // 如果onResolved的返回值是一个Promise对象,间接取它的后果做为promise2的后果 x.then(resolve, reject); } resolve(x); // 否则,以它的返回值做为promise2的后果 } catch (e) { reject(e); // 如果出错,以捕捉到的谬误做为promise2的后果 } })); } // 此处与前一个if块的逻辑简直雷同,区别在于所调用的是onRejected函数,就不再做过多解释 if (self.status === 'rejected') { return (promise2 = new Promise(function(resolve, reject) { try { let x = onRejected(self.value); if (x instanceof Promise) { x.then(resolve, reject); } reject(x); // 否则,以它的返回值做为promise2的后果 } catch (e) { reject(e); } })); } if (self.status === 'pending') { // 如果以后的Promise还处于pending状态,咱们并不能确定调用onResolved还是onRejected, // 只能等到Promise的状态确定后,能力的确如何解决。 // 所以咱们须要把咱们的**两种状况**的解决逻辑做为callback放入promise1(此处即this/self)的回调队列里 // 逻辑自身跟第一个if块内的简直统一,此处不做过多解释 return (promise2 = new Promise(function(resolve, reject) { self.onResolvedCallbacks.push(function(value) { try { let x = onResolved(value); if (x instanceof Promise) { x.then(resolve, reject); } } catch (e) { reject(e); } }); self.onRejectedCallbacks.push(function(reason) { try { let x = onRejected(reason); if (x instanceof Promise) { x.then(resolve, reject); } } catch (e) { reject(e); } }); })); } }}
Promise 链式调用-值透传问题
new Promise(resolve => { resolve(8)}) .then() .then() .then((value) => console.log(value))
从下面的 Promise 应用示例代码中,咱们会发现一个场景:在Promise 链式调用中,当两头的 then 办法没有接管到回调函数时,前面的 then 办法接管的回调函数仍可能获取到后面传递的值。那么这里就须要then 办法在接管的回调函数作如下操作,即如果没有传入胜利/失败回调函数时,默认的回调函数须要将接管的值返回给下一个 Promise 实例对象。上面是对应的实现代码:
then(onResolved, onRejected) { let self = this; let promise2; // 增加值的透传性能 onResolved = typeof onResolved === 'function' ? onResolved : function(value) { return value; }; onRejected = typeof onRejected === 'function' ? onRejected : function(reason) { throw reason; }; ......}
结束语
到这里一个根本的 Promise 曾经实现了,上面是残缺的 Promise 代码实现。经验了整个过程,置信读者对 Promise 的了解会更加深刻了。
class Promise { constructor(executor) { if (typeof executor !== 'function') { throw new TypeError(`Promise resolver ${executor} is not a function`); } this.value = undefined; // Promise的值 this.status = 'pending'; // Promise以后的状态 this.onResolvedCallback = []; // Promise resolve时的回调函数集,因为在Promise完结之前有可能有多个回调增加到它下面 this.onRejectedCallback = []; // Promise reject时的回调函数集,因为在Promise完结之前有可能有多个回调增加到它下面 const resolve = value => { if (this.status === 'pending') { this.status = 'resolved'; this.value = value; for (let i = 0; i < this.onResolvedCallback.length; i++) { this.onResolvedCallback[i](value); } } }; const reject = reason => { if (this.status === 'pending') { this.status = 'rejected'; this.value = reason; for (let i = 0; i < this.onRejectedCallback.length; i++) { this.onRejectedCallback[i](reason); } } }; try { // 思考到执行executor的过程中有可能出错,所以咱们用try/catch块给包起来,并且在出错后以catch到的值reject掉这个Promise executor(resolve, reject); // 执行executor } catch (e) { reject(e); } } // then办法接管两个参数,onResolved,onRejected,别离为Promise胜利或失败后的回调 then(onResolved, onRejected) { let self = this; let promise2; // 增加值的透传性能 onResolved = typeof onResolved === 'function' ? onResolved : function(value) { return value; }; onRejected = typeof onRejected === 'function' ? onRejected : function(reason) { throw reason; }; if (self.status === 'resolved') { // 如果promise1(此处即为this/self)的状态曾经确定并且是resolved,咱们调用onResolved // 因为思考到有可能throw,所以咱们将其包在try/catch块里 return (promise2 = new Promise(function(resolve, reject) { try { let x = onResolved(self.value); if (x instanceof Promise) { // 如果onResolved的返回值是一个Promise对象,间接取它的后果做为promise2的后果 x.then(resolve, reject); } else { resolve(x); // 否则,以它的返回值做为promise2的后果 } } catch (e) { reject(e); // 如果出错,以捕捉到的谬误做为promise2的后果 } })); } // 此处与前一个if块的逻辑简直雷同,区别在于所调用的是onRejected函数,就不再做过多解释 if (self.status === 'rejected') { return (promise2 = new Promise(function(resolve, reject) { try { let x = onRejected(self.value); if (x instanceof Promise) { x.then(resolve, reject); } else { reject(x); // 否则,以它的返回值做为promise2的后果 } } catch (e) { reject(e); } })); } if (self.status === 'pending') { // 如果以后的Promise还处于pending状态,咱们并不能确定调用onResolved还是onRejected, // 只能等到Promise的状态确定后,能力的确如何解决。 // 所以咱们须要把咱们的**两种状况**的解决逻辑做为callback放入promise1(此处即this/self)的回调队列里 // 逻辑自身跟第一个if块内的简直统一,此处不做过多解释 return (promise2 = new Promise(function(resolve, reject) { self.onResolvedCallback.push(function(value) { try { let x = onResolved(value); if (x instanceof Promise) { x.then(resolve, reject); } else { resolve(x); } } catch (e) { reject(e); } }); self.onRejectedCallback.push(function(reason) { try { let x = onRejected(reason); if (x instanceof Promise) { x.then(resolve, reject); } else { reject(x); } } catch (e) { reject(e); } }); })); } }}
参考资料
- Promises/A+ 标准
- Promises/A+ 标准(译)