Promise 源码分析
前言
then/promise
项目是基于Promises/A+
标准实现的Promise
库,从这个项目当中,我们来看Promise
的原理是什么,它是如何做到的,从而更加熟悉Promise
分析
从index.js当中知道,它是先引出了./core.js
,随后各自执行了其他文件的代码,通过requeire
的方法。
我们首先先想一下最基础的promise用法
new Promise((resolve, reject) => { resolve(4); }).then(res => { console.log(res); // export 4 });
Promise中的标准
标准中规定:
- Promise对象初始状态为
Pending
,在被resolve
或reject
时,状态变为Fulfilled
或Rejected
resolve
接收成功的数据,reject
接收失败或错误的数据Promise
对象必须有一个then
方法,且只接受两个可函数参数onFulfilled
、onRejected
index.js
'use strict'; module.exports = require('./core.js'); require('./done.js'); require('./finally.js'); require('./es6-extensions.js'); require('./node-extensions.js'); require('./synchronous.js');
我们先看src/core.js
function Promise(fn) { // 判断 this一定得是object不然就会报错,这个方法一定得要new出来 if (typeof this !== 'object') { throw new TypeError('Promises must be constructed via new'); } // 判断fn 一定得是一个函数 if (typeof fn !== 'function') { throw new TypeError('Promise constructor\'s argument is not a function'); } this._deferredState = 0; this._state = 0; this._value = null; this._deferreds = null; if (fn === noop) return; // 最终doResolve很关键 doResolve(fn, this); }
Promise
是一个构造方法,开始时,它进行了校验,确保了fn
是一个函数,随后对一些变量进行了初始化,最后执行了doResolve()
我们接着看doResolve
这个方法。
/** * Take a potentially misbehaving resolver function and make sure * onFulfilled and onRejected are only called once. * * Makes no guarantees about asynchrony. */ // // 确保`onFulfilled`和`onRejected`方法只调用一次 // 不保证异步 function doResolve(fn, promise) { var done = false; var res = tryCallTwo(fn, function (value) { // 如果done 为true 则return if (done) return; done = true; // 回调执行 resolve() resolve(promise, value); }, function (reason) { // 如果done 为true 则return if (done) return; done = true; reject(promise, reason); }); // res为truCallTwo()的返回值 // 如果done没有完成 并且 res 是 `IS_ERROR`的情况下 // 也会执行reject(),同时让done完成 if (!done && res === IS_ERROR) { done = true; reject(promise, LAST_ERROR); } }
doResolve
最关键的是执行了tryCallTwo
方法,这个方法的第二,第三个参数都是回调,当执行回调后,done
为true,同时各自会执行resolve()
或者reject()
方法。最后当tryCallTwo
的返回值为IS_ERROR
时,也会执行reject()
方法。
我们先来看一下tryCallTwo
方法
function tryCallTwo(fn, a, b) { try { fn(a, b); } catch (ex) { LAST_ERROR = ex; return IS_ERROR; } }
fn
实际就是Promise
初始化时的匿名函数(resolve, reject) => {}
,a
,b
则代表的是resolve()
和reject()
方法,当我们正常执行完Promise
函数时,则执行的是resolve
则在doResolve中
,我们当时执行的第二个参数被回调,如果报错,reject()
被执行,则第二个参数被回调。最后捕获了异常,当发生了报错时,会return IS_ERROR
,非报错时会return undinfed
再回到刚才的doResolve
方法,当执行了第二个参数的回调之后,会执行resolve
方法
function resolve(self, newValue) { // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure // 不能吃传递自己 if (newValue === self) { // 报错 return reject( self, new TypeError('A promise cannot be resolved with itself.') ); } // promise作为参数 if ( newValue && (typeof newValue === 'object' || typeof newValue === 'function') ) { // 获取它的promise方法 读取newValue.then var then = getThen(newValue); if (then === IS_ERROR) { // 如果then IS_ERROR return reject(self, LAST_ERROR); } if ( // 如果then是self的then // 并且Promise then === self.then && // newValue 属于Promise newValue instanceof Promise ) { // _state为3 // 一般then之后走这里 // 执行then(newValue)返回了promise self._state = 3; // selft.value为newValue self._value = newValue; // 当state为3时执行 finale finale(self); return; } else if (typeof then === 'function') { doResolve(then.bind(newValue), self); return; } } self._state = 1; self._value = newValue; finale(self); }
在没有链式调用then
的情况下(也就是只要一个then
)的情况下,会将内部状态_state
设置成3
,将传入值赋给内部变量_value
最后会执行final()
方法,不然则会使用doResolve
来调用then
我们再来看下reject
function reject(self, newValue) { // _state = 2为reject self._state = 2; self._value = newValue; if (Promise._onReject) { Promise._onReject(self, newValue); } finale(self); }
在reject
当中我们的_state
变更为了2,同样最后finale
被调用。
我们来看下finale
函数
// 执行自己的deferreds function finale(self) { if (self._deferredState === 1) { handle(self, self._deferreds); self._deferreds = null; } if (self._deferredState === 2) { for (var i = 0; i < self._deferreds.length; i++) { // 遍历handle handle(self, self._deferreds[i]); } // 将deferred 置空 self._deferreds = null; } }
在该方法当中根据不同的_deferredState
,会执行不同的handle
方法。
我们再来看handle
方法
function handle(self, deferred) { while (self._state === 3) { self = self._value; } // 如果有onHandle方法 则执行该方法 if (Promise._onHandle) { Promise._onHandle(self); } // (初始 _state 为0) if (self._state === 0) { // (初始 _deferredState 为0) if (self._deferredState === 0) { self._deferredState = 1; self._deferreds = deferred; return; } // 如果 _deferredState是1 则__deferreds是一个数组 if (self._deferredState === 1) { self._deferredState = 2; self._deferreds = [self._deferreds, deferred]; return; } // 当走到这里 _deferredState应该是2 将deferred // 插入到数组当中 self._deferreds.push(deferred); return; } handleResolved(self, deferred); }
这里比较关键的应该就是通过deferredState
不同的状态,将deferred
放入deferreds
当中。另外当我们的_state
不为0
时,最终会执行handleResolved
。
继续看handleResolve()
方法
function handleResolved(self, deferred) { asap(function() { // _state为1时,cb = onFulfilled 否则 cb = onRejected var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; if (cb === null) { if (self._state === 1) { resolve(deferred.promise, self._value); } else { reject(deferred.promise, self._value); } return; } var ret = tryCallOne(cb, self._value); if (ret === IS_ERROR) { reject(deferred.promise, LAST_ERROR); } else { resolve(deferred.promise, ret); } }); }.then((res) => { }).catch((error) => { })
在这个方法当中,会根据我们任务(_state)的不同状态,来执行onFulfilled
或者onRejected
方法。当此方法调用时,也就是我们一个简单的Promise
的结束。
回到刚才说的Promise
构造方法结束的时候
设置了Promise
函数的一些变量
Promise._onHandle = null; Promise._onReject = null; Promise._noop = noop;
随后在Promise
的原型上设置了then
方法。
Promise.prototype.then = function(onFulfilled, onRejected) { // 首先看这是谁构造的 如果不是promise // 则return 执行safeThen if (this.constructor !== Promise) { return safeThen(this, onFulfilled, onRejected); } // 如果是则初始化一个Promise 但是参数 noop 为空对象 {} var res = new Promise(noop); // 随后执行handle方法 handle(this, new Handler(onFulfilled, onRejected, res)); return res; };
在then
这个方法中首先判断了它是否由Promise
构造的,如果不是,则返回并执行safeThen
,不然则执行Promise
构造一个res
对象,然后执行handle
方法,最后将Promise
变量res
返回。handle
方法之前有提过,在这里,当初始化时_state
和_deferred
的转改都为0
,因此它会将defrred
保存到Promise
当中。
先看一下上面说的safeThen
方法
function safeThen(self, onFulfilled, onRejected) { return new self.constructor(function (resolve, reject) { var res = new Promise(noop); res.then(resolve, reject); handle(self, new Handler(onFulfilled, onRejected, res)); }); }
流程
需要有一个Promise
的构造方法,这个构造方法最终会执行它的参数(resolve, reject) => {}
,声明的then
方法会通过handle()
方法将onFulfilled
和onRejected
方法保存起来。当在外部调用resolve
或者onRejected
时,最终也会执行handle
但是它,会最后根据状态来执行onFulfilled
或者onRejected
。从而到我们的then
回调中。
Promise的扩展
done
对done
的扩展在src/done.js
当中
'use strict'; var Promise = require('./core.js'); module.exports = Promise; Promise.prototype.done = function (onFulfilled, onRejected) { var self = arguments.length ? this.then.apply(this, arguments) : this; self.then(null, function (err) { setTimeout(function () { throw err; }, 0); }); };
内部执行了then()
finally
对finally
的扩展在src/finally.js
当中
在Promise
的标准当中,本身是没有finally
方法的,但是在ES2018
的标准里有,finally
的实现如下
'use strict'; var Promise = require('./core.js'); module.exports = Promise; Promise.prototype.finally = function (callback) { return this.then(function (value) { return Promise.resolve(callback()).then(function () { return value; }); }, function (err) { return Promise.resolve(callback()).then(function () { throw err; }); }); };
Promise
的onFulfilled
和onRejected
不管回调的哪个,最终都会触发callback
回调。还要注意的一点是finally
的返回也是一个Promise
。
es6-extensions.js
在es6-extensions.js
文件当中包含了ES6的一些扩展。
Promise.resolve
function valuePromise(value) { var p = new Promise(Promise._noop); // 将_state赋值为 非0 // _value进行保存 p._state = 1; p._value = value; // 这样做的目的是省略的一些前面的逻辑 return p; } Promise.resolve = function (value) { if (value instanceof Promise) return value; if (value === null) return NULL; if (value === undefined) return UNDEFINED; if (value === true) return TRUE; if (value === false) return FALSE; if (value === 0) return ZERO; if (value === '') return EMPTYSTRING; // value return new Promise if (typeof value === 'object' || typeof value === 'function') { try { var then = value.then; if (typeof then === 'function') { // 返回 返回了一个新的Promise对象 return new Promise(then.bind(value)); } } catch (ex) { // 如果报错 则返回一个就只 return new Promise(function (resolve, reject) { reject(ex); }); } } return valuePromise(value); };
Promise.reject
Promise.reject = function (value) { return new Promise(function (resolve, reject) { reject(value); }); };
Promise.all
Promise.all = function (arr) { // 类似深拷贝了一份给了args var args = Array.prototype.slice.call(arr); return new Promise(function (resolve, reject) { // 判断了all的promise数量 if (args.length === 0) return resolve([]); // remaining则是promise数组的长度 var remaining = args.length; // i为index val 为 promise function res(i, val) { if (val && (typeof val === 'object' || typeof val === 'function')) { if (val instanceof Promise && val.then === Promise.prototype.then) { while (val._state === 3) { val = val._value; } if (val._state === 1) return res(i, val._value); if (val._state === 2) reject(val._value); // val._state 为 0时 走这里 val.then(function (val) { res(i, val); }, reject); return; } else { var then = val.then; if (typeof then === 'function') { var p = new Promise(then.bind(val)); p.then(function (val) { res(i, val); }, reject); return; } } } args[i] = val; // 当所有的promise执行完 则是remaining为0 // 则执行resolve(); if (--remaining === 0) { resolve(args); } } // 遍历所有的promise for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); };
Promise.all()
返回的也是一个Promise
函数。
内部有一个remaining
变量每当执行完一个Promise
函数后就会减一,当所有Promise
执行完,会执行自己的resolve
。
Promise.race
Promise.race = function (values) { return new Promise(function (resolve, reject) { values.forEach(function(value){ Promise.resolve(value).then(resolve, reject); }); }); };
遍历传入的Promise
数组,经过Promise.resolve(value)
的源码可以看到,如果value
是一个Promise
则户直接将这个value
返回,最后数组中的Promise
哪个优先回调即执行。
Promise.property.catch
catch
在标准当中也是没有,虽然我们用的比较多
Promise.prototype['catch'] = function (onRejected) { return this.then(null, onRejected); };
catch
的回调实际是then(null, onRejected)
的回调。
广而告之
本文发布于薄荷前端周刊,欢迎Watch & Star ★,转载请注明出处。