共计 8901 个字符,预计需要花费 23 分钟才能阅读完成。
前言
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 ★,转载请注明出处。
欢迎讨论,点个赞再走吧 。◕‿◕。 ~