这篇文章旨在解析 Promise的异步实现原理,而且以 ES6中的 Promise 为蓝本实现一个简单的 Promise。git
经过本身动手实现一个 Promise 对象,能够熟悉不少可能不知道的 Promise 细节,同时也能对异步的理解更提高一步。es6
本文假设读者对 Promise 规范有必定理解,而且熟悉 ES6 中的 Promise 基本操做。github
Promise 规范中规定了,promise 的状态只有3种:promise
顾名思义,对上面3个状态的解释就再也不赘述,Promise 的状态一旦改变则不会再改变。浏览器
具体的规范能够参见:https://promisesaplus.comdom
这部份内容参考: http://es6.ruanyifeng.com/#do...异步
作了上面的铺垫,实现一个 Promise 的思路就清晰不少了,本文使用 ES6 来进行实现,暂且把这个类取名为 GPromise吧(不覆盖原生的,便于和原生进行对比测试)。下文中 GPromise 代指将要实现的类,Promise 代指 ES6中的 Promise 类。async
在浏览器中打印出一个 Promise 实例会发现其中会包括两用"[[ ]]"包裹起来的属性,这是系统内部属性,只有JS 引擎可以访问。函数
[[PromiseStatus]] [[PromiseValue]]
以上两个属性分别是 Promise 对象的状态和最终值。测试
咱们本身不能实现内部属性,JS中私有属性特性(#修饰符如今仍是提案)暂时也没有支持,因此暂且用"_"前缀规定私有属性,这样就模拟了Promise 中的两个内部属性。
class GPromise { constructor(executor) { this._promiseStatus = GPromise.PENDING; this._promiseValue; this.execute(executor); } execute(executor){ //... } then(onfulfilled, onrejected){ //... } } GPromise.PENDING = 'pedding'; GPromise.FULFILLED = 'resolved'; GPromise.REJECTED = 'rejected';
execute(executor) { if (typeof executor != 'function') { throw new Error(` GPromise resolver ${executor} is not a function`); } //捕获错误 try { executor(data => { this.promiseStatus = GPromise.FULFILLED; this.promiseValue = data; }, data => { this.promiseStatus = GPromise.REJECTED; this.promiseValue = data; }); } catch (e) { this.promiseStatus = GPromise.REJECTED; this.promiseValue = e; } }
注:Promise 对象在executor 发生错误或者reject 时,若是没有then
或者 catch 来处理,会把错误抛出到外部,也就是会报错。GPromise 实现的是没有向外部抛出错误,只能由then方法处理。
then 方法内部逻辑稍微复杂点,而且有一点必定必定必定要注意到: then 方法中的回调是异步执行的,思考下下段代码:
console.log(1); new Promise((resolve,reject)=>{ console.log(2); resolve(); }) .then(()=>console.log(3)); console.log(4);
执行结果是什么呢?答案实际上是:1 2 4 3。传入Promise 中的执行函数是当即执行完的啊,为何不是当即执行 then 中的回调呢?由于then 中的回调是异步执行,表示该回调是插入事件队列末尾,在当前的同步任务结束以后,下次事件循环开始时执行队列中的任务。
then 方法中的难点就是处理异步,其中一个方案是经过 setInterval来监听GPromise 对象的状态改变,一旦改变则执行相应then 中相应的回调函数(onfulfilled和onrejected),这样回调函数就可以插入事件队列末尾,异步执行,实验证实可行,这种方案是最直观也最容易理解的。
then 方法的返回值是一个新的 GPromise 对象,而且这个对象的状态和 then 中的回调返回值相关,回调指代传入的 onfulfilled 和 rejected。
then 方法中的重点逻辑如上,其余参见代码便可:
then(onfulfilled, onrejected) { let _ref = null, timer = null, result = new GPromise(() => {}); //由于 promise 的 executor 是异步操做,须要监听 promise 对象状态变化,而且不能阻塞线程 timer = setInterval(() => { if ((typeof onfulfilled == 'function' && this._promiseStatus == GPromise.FULFILLED) || (typeof onrejected == 'function' && this._promiseStatus == GPromise.REJECTED)) { //状态发生变化,取消监听 clearInterval(timer); //捕获传入 then 中的回调的错误,交给 then 返回的 promise 处理 try { if (this._promiseStatus == GPromise.FULFILLED) { _ref = onfulfilled(this._promiseValue); } else { _ref = onrejected(this._promiseValue); } //根据回调的返回值来决定 then 返回的 GPromise 实例的状态 if (_ref instanceof GPromise) { //若是回调函数中返回的是 GPromise 实例,那么须要监听其状态变化,返回新实例的状态是根据其变化相应的 timer = setInterval(()=>{ if (_ref._promiseStatus == GPromise.FULFILLED || _ref._promiseStatus == GPromise.REJECTED) { clearInterval(timer); result._promiseValue = _ref._promiseValue; result._promiseStatus = _ref._promiseStatus; } },0); } else { //若是返回的是非 GPromise 实例 result._promiseValue = _ref; result._promiseStatus = GPromise.FULFILLED; } } catch (e) { //回调中抛出错误的状况 result._promiseStatus = GPromise.REJECTED; result._promiseValue = e; } } }, 0); //promise 之因此可以链式操做,由于返回了GPromise对象 return result; }
是骡子是马,拉出来溜溜。。
测试环境是macOS Sierra 10.12.6,Chrome 60.0.3112.113。
通过如下测试, 证实了GPromise 的基本的异步流程管理和原生 Promise 没有差异。如下测试用例参考了 MDN 中的[Promise
API](https://developer.mozilla.org... 中的 Advanced Example。
var promiseCount = 0; function test(isPromise) { let thisPromiseCount = ++promiseCount, executor = (resolve, reject) => { console.log(thisPromiseCount + ') Promise started (Async code started)'); window.setTimeout( function () { resolve(thisPromiseCount); }, Math.random() * 2000 + 1000); }; console.log(thisPromiseCount + ') Started (Sync code started)'); let p1 = isPromise ? new Promise(executor) : new GPromise(executor); p1.then( function (val) { console.log(val + ') Promise fulfilled (Async code terminated)'); }, function (reason) { console.log('Handle rejected promise (' + reason + ') here.'); }); console.log(thisPromiseCount + ') Promise made (Sync code terminated)'); } test(); test(true); test();
那么再来测试下链式操做(没有链式操做的 Promise 我要你有何用?),测试结果和 Promise 表现一致。
function async1() { return new GPromise( (resolve, reject) => { console.log('async1 start'); setTimeout(() => { resolve('async1 finished') }, 1000); } ); } function async2() { return new GPromise( (resolve, reject) => { console.log('async2 start'); setTimeout(() => { resolve('async2 finished') }, 1000); } ); } function async3() { return new GPromise( (resolve, reject) => { console.log('async3 start'); setTimeout(() => { resolve('async3 finished'); }, 1000); } ); } async1() .then( data => { console.log(data); return async2(); }) .then( data => { console.log(data); return async3(); } ) .then( data => { console.log(data); } );
到此为止,一个高仿的 Promise 已经实现完成了,它很简单,由于只有一个 then 方法,异步的状态管理由内部完成。
这里并无实现 catch方法,由于上文也提到了,catch方法就至关于 then(null,onrejected) 。并且 Promise 类上的 race,all,resolve,reject也没有实现,本文旨在理清 Promise 核心原理,篇幅受限(其实就是我懒),其余辅助类的方法等以后有时间再实现。
本文提供的只是一个思路,但愿能帮助到你,欢迎你们批评指教。
代码地址:Github