若是要手写实现promise,那么先看看promise/A+规范,再来实现,将会事半功倍。
那么我先翻译一下Promise/A+规范中的内容。git
要求
2.1 promise 状态 github
一个promise必须是三种状态其中的一种状态:pending,fulfilled 或者 rejected。 算法
2.1.1 当promise处于pending状态时:npm
2.1.1.1 能够转变到 fulfilled 或者 rejected 状态。
2.1.2 当promise处于fulfilled状态时:promise
2.1.2.1 必定不可以转变到其余任何一种状态。 2.1.2.2 必须有一个value,而且这个值必定不能改变。
2.1.3 当promise处于rejected状态时:异步
2.1.3.1 必定不可以转变到其余任何一种状态。 2.1.3.2 必须有一个reason,而且这个值不可以改变。
在这里,“必定不能改变”意味着不可变的身份(即===),但并不意味着深层不变性。(我的理解为是value/reason指向的地址是不可变的,但倘若value/reason为一个对象,则对象内的值是可变的。)函数
2.2 then 方法
一个promise必须提供一个then方法去访问当前或者最终的value或者reason。
一个promise的then方法接受两个参数:测试
promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled 和 onRejected 都是可选的参数:this
2.2.1.1 若是 onFulfilled 不是一个函数,它必须被忽略。 2.2.1.2 若是 onRejected 不是一个函数,它必须被忽略。
2.2.2 若是 onFulfilled 是一个函数:spa
2.2.2.1 promise 是 fulfilled 状态时它必须被调用,而且 promise 的 value 做为它的第一个参数。 2.2.2.2 它必定不能在 promise 进入 fulfilled 状态以前被调用。 2.2.2.3 它必定不可以调用超过一次。
2.2.3 若是 onRejected 时一个函数:
2.2.3.1 promise 是 rejected 状态时它必须被调用,而且 promise 的 reason 做为它的第一个参数。 2.2.3.2 它必定不能在 promise 进入 rejected 状态以前被调用。 2.2.3.3 它必定不可以调用超过一次。
2.2.4 直到执行上下文堆栈仅包含平台代码以前,onFulfilled 或 onRejected 不可以被调用。[3.1]
2.2.5 onFulfilled 和 onRejected 必须以函数的形式被调用(即没有this值)。[3.2]
2.2.6 then 能够在同一个promise被调用屡次:
2.2.6.1 当 promise 处于 fulfilled 状态时,各个 onFulfilled 回调必须按其原始调用的顺序执行。 2.2.6.2 当 promise 处于 rejected 状态时,各个 onRejected 回调必须按其原始调用的顺序执行。
2.2.7 then 必须返回一个promise [3.3]:
promise2 = promise1.then(onFulfilled, onRejected);
2.2.7.1 若是 onFulfilled 或 onRejected 返回一个值 x,运行Promise解决程序 [[Resolve]](promise2, x)。 2.2.7.2 若是 onFulfilled 或 onRejected 抛出一个意外 e,promise2 必须以 e 为 reason 被 rejected。 2.2.7.3 若是 onFulfilled 不是一个函数而且 promise1 处于 fulfilled 状态,promise2 必须以与 promise1 一样的 value 转变到 fulfilled 状态。 2.2.7.4 若是 onRejected 不是一个函数而且 promise1 处于 rejected状态,promise2 必须以与 promise1 一样的 reason 转变到 rejected状态。
2.3 Promise解决程序
promise解决程序是一个抽象的操做,它把一个 promise 和一个 value 做为输入,咱们将这个表示为 [[Resolve]](promise, x)。若是 x 是一个 thenable ,它将会试图让 promise 采用 x 的状态,前提是x的行为至少有点像一个 promise。不然,它将会用值 x 执行 promise。
对这些 thenable 的处理使得与 promise 实现方式可以去互相操做。只要它们公开了符合 Promise/A+ 的 then 方法。它还使得 promises/A+ 实现方式可以采用合理的 then 方法去“同化”不一致的实现方式。
为了运行[[Resolve]](promise, x),执行如下步骤:
2.3.1 若是 promise 与 x 是同一个对象,以 Tyeperror 做为 reason 去 reject promise。
2.3.2 若是 x 是一个 promise,使用它的状态:
2.3.2.1 若是 x 处于 pending 状态,promise 必须保持 pending 状态直到 x 处于 fulfilled 或者 rejected 状态。 2.3.2.2 若是 x 处于 fulfilled 状态,以相同的 value 去 fulfill promise。 2.3.2.3 若是 x 处于 rejected 状态,以相同的 reason 去 reject promise。
2.3.3 不然,若是 x 是一个对象或者函数:
2.3.3.1 让 then 做为 x.then。 2.3.3.2 若是取属性 x.then 会致使抛出异常 e,则以 e 为 reason reject promise。 2.3.3.3 若是 then 是一个函数,让 x 做为 this 调用它,第一个参数为 resolvePromise,第二个参数为 rejectPromise,而后: 2.3.3.3.1 若是使用value y 调用 resolvepromise 时,运行[[Resolve]](promise, y)。 2.3.3.3.2 若是使用reason r 调用 rejectPromise 时,也用 r reject promise。 2.3.3.3.3 若是 resolvePromise 和 rejectPromise 都被调用了,或屡次调用同一参数,那么第一个调用优先,其余的调用都会被忽略。 2.3.3.3.4 若是调用 then 的过程当中抛出了一个意外 e, 2.3.3.3.4.1 若是 resolvePromise 或者 rejectPromise 被调用了,那么忽略它。 2.3.3.3.4.2 不然,把 e 做为 reason reject promise。 2.3.3.4 若是 then 不是一个函数,将 x 做为参数执行 promise。
2.3.4 若是 x 不是一个对象或者函数,将 x 做为参数执行 promise。
若是一个参与了 thenable 循环链的 thenable 去 resolve promise,这样 [[Resolve]](promise, thenable) 的递归性质最终会致使 [[Resolve]](promise, thenable) 会被再次调用,遵循上述算法将会致使无限递归。咱们鼓励去实现(但不是必需的)检测这样的递归,并以 TypeError 做为 reason 去 reject Promise。[3.6]
接下来根据规范进行手写实现,注释偷懒就将对应的规范标注出来,其实基本上就是对着规范实现。
function promise(fuc){ //接收一个函数做为参数 let that = this; that.value = null; // 2.1.2.2 that.reason = null;// 2.1.3.2 that.onFulfilled = []; // 2.2.6 that.onRejected = []; //2.2.6 that.status = 'pending'; // 2.1 function resolve(val){ if (that.status === 'pending') { // 2.1.2 that.status = 'fulfilled'; that.value = val; that.onFulfilled.forEach(fc => fc(val)); //2.2.6.1 } } function reject(err){ if (that.status === 'pending') { //2.1.3 that.status = 'rejected'; that.reason = err; that.onRejected.forEach(fc => fc(err)); //2.2.6.2 } } try { fuc(resolve,reject); } catch (error) { reject(error); } } promise.prototype.then = function (onFulfilled, onRejected) //2.2 { let that = this, promise2; //2.2.7 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (x) => x; //2.2.1 2.2.7.3 onRejected = typeof onRejected === 'function' ? onRejected : (e) => { throw e }; // 2.2.7.4 switch (that.status) { case 'pending': promise2 = new promise((reslove, reject)=>{ that.onFulfilled.push(()=>{ setTimeout(() => { try { let x = onFulfilled(that.value); //2.2.2.1 ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1 } catch (err) { reject(err); //2.2.7.2 } }, 0); }); that.onRejected.push(()=>{ setTimeout(() => { try { let x = onRejected(that.reason); //2.2.3.1 ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1 } catch (err) { reject(err); //2.2.7.2 } }, 0); }); }) break; case 'fulfilled': promise2 = new promise((reslove,reject)=>{ setTimeout(() => { // 2.2.4 try{ let x = onFulfilled(that.value); //2.2.2.1 ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1 }catch(err){ reject(err); //2.2.7.2 } }, 0); }) break; case 'rejected': promise2 = new promise((reslove, reject)=>{ setTimeout(() => { // 2.2.4 try{ let x = onRejected(that.reason); //2.2.3.1 ResolutionProcedure(promise2, x, reslove, reject); //2.2.7.1 }catch(err){ reject(err); //2.2.7.2 } }, 0); }) break; default: break; } return promise2; } function ResolutionProcedure(promise, x, reslove, reject){ //2.3 if (promise === x) //2.3.1 return reject(new TypeError("same promise")); if (x instanceof Promise) //2.3.2 x.then(((xvalue)=>{ ResolutionProcedure(promise, xvalue, reslove, reject); },(xreason)=>{ reject(xreason) })); if (x !== null &&( typeof x === 'object' || typeof x === 'function' )) //2.3.3 { try { let then = x.then;//2.3.3.1 if(typeof then === 'function') //2.3.3.3 { let isuse = false; try { then.call(x,(y)=>{ if (!isuse) { ResolutionProcedure(promise, y, reslove, reject); //2.3.3.3.1 isuse = true; } },(r)=>{ if (!isuse) { reject(r) //2.3.3.3.2 isuse = true; } }) } catch (error) { if(!isuse) reject(error) //2.3.3.3.4.2 } }else{ reslove(x); //2.3.3.4 } } catch (error) { reject(error); //2.3.3.2 } }else{ reslove(x); //2.3.4 } }
作完以后能够经过 promise test 进行测试。
先在代码下面加上一些测试须要代码。
promise.deferred = function () { let def = {}; def.promise = new promise(function (resolve, reject) { def.resolve = resolve; def.reject = reject; }); return def; } module.exports = promise
而后跑一下下面的代码便可。
npm install -g promises-aplus-tests promises-aplus-tests promise.js
结果所有经过,说明符合规范: