本文遵循的 Promise/A+
标准实现一个简略版本 Promise, 用代码了解标准中的每一句话.
Promise/A+
英文版标准, 中文版标准;- es6 中 Promise 残缺标准
- 浏览器中 Promise 实现
Promise 的状态
标准形容
一个 Promise 的以后状态必须为以下三种状态中的一种:期待态(Pending)、实现态(Fulfilled)和 回绝态(Rejected)。
-
期待态(Pending)
处于期待态时,promise 需满足以下条件:- 能够迁徙至执行态或回绝态
-
实现态(Fulfilled)
处于执行态时,promise 需满足以下条件:- 不能迁徙至其余任何状态
- 必须领有一个不可变的终值
-
回绝态(Rejected)
处于回绝态时,promise 需满足以下条件:- 不能迁徙至其余任何状态
- 必须领有一个不可变的据因
代码实现
采纳类实现:
class MyPromise {}
实现构造函数:
const _ = require('lodash');
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
class MyPromise {constructor(executor) {if (!(this instanceof MyPromise)) {throw new TypeError("Failed to construct'Promise': Please use the'new'operator, this object constructor cannot be called as a function.");
}
if (!_.isFunction(executor)) {throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}
this._status = PENDING;
this._value = undefined;
this._thenCallbacks = [];
this._reason = undefined;
try {executor((...args) => this._resolve(...args), (...args) => this._reject(...args));
} catch(e) {this._reject(e);
}
}
}
在构造函数中, 首先处理函数边界, 而后初始化状态, 终值 (_value), 拒因(_reason) 和then
的回调函数队列; 最初调用传入的 executor
.executor
中用于管制 Promise
状态的变动. 传递两个回调函数作为参数,第一个参数叫做resove
,第二个参数叫做 reject
, 在对象中的解决解决逻辑为:
_resolve(value) {if (this._status !== PENDING) {return;}
this._status = FULFILLED;
this._value = value;
}
_reject(reason) {if (this._status !== PENDING) {return;}
this._status = REJECTED;
this._reason = reason;
}
_resolve
, _reject
有状态边界判断, 如果被调用屡次, 采纳首次调用并疏忽剩下的调用._resolve
中只容许期待态转为实现态, 而后接管终值._reject
中只容许期待态转为回绝态, 而后接管拒因.
状态的判断保障
_resolve
,_reject
中次要逻辑在以后promise
中最多被执行一次: 状态最多扭转一次;then
的回调函数最多调用一次.
Then 函数
标准形容
一个 promise
必须提供一个 then
办法以拜访其以后值、终值和据因。promise
的 then
办法承受两个参数:
promise.then(onFulfilled, onRejected)
参数可选
onFulfilled
和 onRejected
都是可选参数。
- 如果
onFulfilled
不是函数,其必须被疏忽 - 如果
onRejected
不是函数,其必须被疏忽
onFulfilled
个性
如果 onFulfilled
是函数:
- 当
promise
执行完结后其必须被调用,其第一个参数为promise
的终值 - 在
promise
执行完结前其不可被调用 - 其调用次数不可超过一次
调用机会
onFulfilled
和 onRejected
只有在执行环境堆栈仅蕴含平台代码时才可被调用
调用要求
onFulfilled
和 onRejected
必须被作为函数调用(即没有 this 值)注 2
屡次调用
then
办法能够被同一个 promise
调用屡次
当 promise
胜利执行时,所有 onFulfilled
需依照其注册程序顺次回调
当promise
被拒绝执行时,所有的 onRejected
需依照其注册程序顺次回调
返回
then
办法必须返回一个 promise
对象
promise2 = promise1.then(onFulfilled, onRejected);
- 如果
onFulfilled
或者onRejected
返回一个值 x,则运行上面的Promise
解决过程:[[Resolve]](promise2, x)
- 如果
onFulfilled
或者onRejected
抛出一个异样e
,则promise2
必须拒绝执行,并返回拒因e
- 如果
onFulfilled
不是函数且promise1
胜利执行,promise2
必须胜利执行并返回雷同的值 - 如果 onRejected 不是函数且
promise1
拒绝执行,promise2
必须拒绝执行并返回雷同的据因
代码实现
须要在构造函数增加回调函数队列:
constructor(executor) {
// ...
this._value = undefined;
this._thenCallbacks = [];
this._reason = undefined;
// ...
}
首先, 实现一个 then
函数. 因为本章节代码牵扯到很多局部, 所以尽量用代码正文来阐明实现的标准:
then(onFulfilled, onRejected) {
// 如果 onFulfilled 不是函数,其必须被疏忽
const _onFulfilled = _.isFunction(onFulfilled) ? onFulfilled : void 0;
// 如果 onRejected 不是函数,其必须被疏忽
const _onRejected = _.isFunction(onRejected) ? onRejected : void 0;
// then 办法能够被同一个 promise 调用屡次
this._thenCallbacks.push([_onFulfilled, _onRejected]);
return new MyPromise((resolve, reject) => {// 期待实现});
}
onFulfilled
和 onRejected
, 须要在_resove
或者 reject
时被调用, 将 resolve
, reject
革新为:
_resolve(value) {if (this._status !== PENDING) {return;}
this._status = FULFILLED;
this._value = value;
// 如果 then 的回调函数 onFulfilled, onRejected 为函数的话, 须要
// 在 promise 执行完结前其不可被调用, 当 promise 执行完结后其必须被调用
// 其调用次数不可超过一次
// 只有在执行环境堆栈仅蕴含平台代码时才可被调用
process.nextTick(this._callThenCallbacks);
}
_reject(reason) {if (this._status !== PENDING) {return;}
this._status = REJECTED;
this._reason = reason;
// 如果 then 的回调函数 onFulfilled, onRejected 为函数的话, 须要
// 在 promise 执行完结前其不可被调用, 当 promise 执行完结后其必须被调用
// 其调用次数不可超过一次
// 只有在执行环境堆栈仅蕴含平台代码时才可被调用
process.nextTick(this._callThenCallbacks);
}
尽管标准中没有阐明 onFulfilled
和onRejected
必须为微工作 (micro-task) 还是宏工作(macro-task), 然而其余实现根本都是基于微工作.
这里因为本文只是在 node 环境实现, 所以采纳 process.nextTick
来启用微工作, 为了跨平台, 个别的 Promise 实现框架, 都会应用多种形式来实现在执行环境堆栈仅蕴含平台代码时才可被调用, 如 MutationObserver
, MessageChannel
, vertx
等, 最初可能应用 setTimeout
实现.
依照 Promise/A+
标准来说,onFulfilled
和 onRejected
只有在执行环境堆栈仅蕴含平台代码时才可被调用, 然而在 nodejs
或者 es6 环境中的 Promise
对象, 须要像上面的实现:
_resolve(value) {
// 只有在执行环境堆栈仅蕴含平台代码时才可被调用
process.nextTick(() => {if (this._status !== PENDING) {return;}
this._status = FULFILLED;
this._value = value;
// 如果 then 的回调函数 onFulfilled, onRejected 为函数的话, 须要
// 在 promise 执行完结前其不可被调用, 当 promise 执行完结后其必须被调用
// 其调用次数不可超过一次
// 只有在执行环境堆栈仅蕴含平台代码时才可被调用
// process.nextTick(() => this._callThenCallbacks());
this._callThenCallbacks();});
}
_reject(reason) {
// 只有在执行环境堆栈仅蕴含平台代码时才可被调用
process.nextTick(() => {if (this._status !== PENDING) {return;}
this._status = REJECTED;
this._reason = reason;
// 如果 then 的回调函数 onFulfilled, onRejected 为函数的话, 须要
// 在 promise 执行完结前其不可被调用, 当 promise 执行完结后其必须被调用
// 其调用次数不可超过一次
// process.nextTick(() => this._callThenCallbacks());
this._callThenCallbacks();});
}
对于比拟风行的 prmise polyfill 库
es-promise
的实现, 采纳的是下面一种启用微工作的机会, 对于 babel 中的垫片 core-js 中实现的promise
, 采纳的是下一种启用机会.
Then 函数. 返回标准有简单的要求, 为了实现这些要求, 须要扭转下面的 then
函数的实现:
then(onFulfilled, onRejected) {
// 如果 onFulfilled 不是函数,其必须被疏忽
const _onFulfilled = _.isFunction(onFulfilled) ? onFulfilled : void 0;
// 如果 onRejected 不是函数,其必须被疏忽
const _onRejected = _.isFunction(onRejected) ? onRejected : void 0;
let childResolve;
let childReject;
const childPromise = new MyPromise((resolve, reject) => {
childResolve = resolve;
childReject = reject;
});
// then 办法能够被同一个 promise 调用屡次
this._thenCallbacks.push([_onFulfilled, _onRejected, childResolve, childReject]);
return childPromise;
}
_callThenCallbacks
用于解决在 promise
状态扭转后处理 then
回调函数队列. 在解决每一个 then
回调函数后, 还须要对于 then
回调函数返回的后果, 联合以后的 promise
状态, 调整以后 then
函数返回的 promise2
的状态:
// 调用 then 回调函数队列
_callThenCallbacks() {if (_.isEmpty(this._thenCallbacks)) {return;}
this._thenCallbacks.forEach(([onFulfilled, onRejected, childResolve, childReject]) => {
try {if (this._status === FULFILLED && !onFulfilled) {
// 如果 onFulfilled 不是函数且 promise1 胜利执行,promise2 必须胜利执行并返回雷同的值
childResolve(this._value);
return;
}
if (this._status === REJECTED && !onRejected) {
// 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回雷同的据因
childReject(this._reason);
}
let x;
if (this._status === REJECTED && onRejected) {
// 当 promise 被拒绝执行时,所有的 onRejected 需依照其注册程序顺次回调
// 其第一个参数为 promise 的拒因
// 必须被作为函数调用(即没有 this 值)x = onRejected(this._reason);
} else if (this._status === FULFILLED && onFulfilled) {
// 当 promise 胜利执行时,所有 onFulfilled 需依照其注册程序顺次回调
// 其第一个参数为 promise 的终值
// 必须被作为函数调用(即没有 this 值)x = onFulfilled(this._value);
}
// 如果 onFulfilled 或者 onRejected 返回一个值 x,则运行上面的 Promise 解决过程
this._resolvePromise(x, childResolve, childReject);
} catch (error) {childReject(error);
}
});
}
其中 _resolvePromise
代表 Promise 解决过程, 将在下文阐明.
Promise 解决过程
标准形容
Promise 解决过程 是一个形象的操作,其需输出一个 promise
和一个值,咱们示意为 [[Resolve]](promise, x)
,如果x
有then
办法且看上去像一个 Promise,解决程序即尝试使 promise
承受 x
的状态;否则其用 x
的值来执行 promise。
这种 thenable
的个性使得 Promise
的实现更具备通用性:只有其暴露出一个遵循 Promise/A+ 协定的 then
办法即可;这同时也使遵循 Promise/A+ 标准的实现能够与那些不太标准但可用的实现能良好共存。
运行 [[Resolve]](promise, x)
需遵循以下步骤:
-
x
与promise
相等
如果x
为 Promise,则使promise 承受
x` 的状态:- 如果
x
处于期待态,promise
需放弃为期待态直至x
被执行或回绝 - 如果
x
处于执行态,用雷同的值执行promise
- 如果
x
处于回绝态,用雷同的据因回绝promise
- 如果
-
x
为对象或函数
如果x
为对象或者函数:- 把
x.then
赋值给then
- 如果取
x.then
的值时抛出谬误e
,则以e
为据因回绝promise
-
如果
then
是函数,将x
作为函数的作用域this
调用之。传递两个回调函数作为参数,第一个参数叫做resolvePromise
,第二个参数叫做rejectPromise
:- 如果
resolvePromise
以值y
为参数被调用,则运行[[Resolve]](promise, y)
- 如果
rejectPromise
以据因r
为参数被调用,则以据因r
回绝promise
- 如果
resolvePromise
和rejectPromise
均被调用,或者被同一参数调用了屡次,则优先采纳首次调用并疏忽剩下的调用 -
如果调用
then
办法抛出了异样e
:- 如果
resolvePromise
或rejectPromise
曾经被调用,则疏忽之 - 否则以
e
为据因回绝promise
- 如果
- 如果
then
不是函数,以x
为参数执行promise
- 如果
- 把
- 如果
x
不为对象或者函数,以x 为参数执行
promise`
如果一个 promise
被一个循环的 thenable
链中的对象解决,而 [[Resolve]](promise, thenable)
的递归性质又使得其被再次调用,根据上述的算法将会陷入有限递归之中。算法虽不强制要求,但也激励施者检测这样的递归是否存在,若检测到存在则以一个可辨认的 TypeError
为据因来回绝promise
.
代码实现
函数 _resolvePromise
实现, 采纳代码正文阐明:
// Promise 解决过程
_resolvePromise(x, childResolve, childReject) {
// x 与 promise 相等
if (x === this) {
// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
throw new TypeError("You cannot resolve a promise with itself");
}
// 如果 x 为 Promise,则使 promise 承受 x 的状态
if (x instanceof MyPromise) {
// 如果 x 处于期待态
console.log('======PENDING===', x._status);
if (x._status === PENDING) {
// promise 需放弃为期待态直至 x 被执行或回绝
x.then(childResolve, childReject);
return;
}
// 如果 x 处于执行态
if (x._status === FULFILLED) {
// 用雷同的值执行 promise
childResolve(x._value);
return;
}
// 如果 x 处于执行态
if (x._status === REJECTED) {
// 用雷同的值执行 promise
childReject(x._reason);
return;
}
}
// x 为对象或函数
if (_.isObject(x) || _.isFunction(x)) {
// 把 x.then 赋值给 then
let then;
try {then = x.then;} catch (error) {
// 如果取 x.then 的值时抛出谬误 e,则以 e 为据因回绝 promise
// 其实这里不须要捕捉, 因为最外层有捕捉, 这里为了放弃跟标准统一
childReject(error);
return;
}
// 如果 then 是函数
if (_.isFunction(then)) {
// 将 x 作为函数的作用域 this 调用之
let called = false;
try {then.call(x, (y) => {
// 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了屡次,
// 则优先采纳首次调用并疏忽剩下的调用
if (called) {return;}
called = true;
// 如果 resolvePromise 以值 y 为参数被调用
this._resolvePromise(y, childResolve, childReject);
}, (r) => {
// 如果 resolvePromise 或 rejectPromise 曾经被调用,则疏忽之
if (called) {return;}
called = true;
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 回绝 promise
childReject(r);
});
} catch (error) {
// 如果调用 then 办法抛出了异样 e
// 如果 resolvePromise 或 rejectPromise 曾经被调用,则疏忽之
if (called) {return;}
// 否则以 e 为据因回绝 promise
childReject(error);
}
return;
}
// 如果 then 不是函数, 以 x 为参数执行 promise
childResolve(x);
return;
}
// 如果 x 不为对象或者函数, 以 x 为参数执行 promise
childResolve(x);
}
全副代码
const _ = require('lodash');
const PENDING = 0;
const FULFILLED = 1;
const REJECTED = 2;
class MyPromise {constructor(executor) {if (!(this instanceof MyPromise)) {throw new TypeError("Failed to construct'Promise': Please use the'new'operator, this object constructor cannot be called as a function.");
}
if (!_.isFunction(executor)) {throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}
this._status = PENDING;
this._value = undefined;
this._thenCallbacks = [];
this._reason = undefined;
try {executor((...args) => this._resolve(...args), (...args) => this._reject(...args));
} catch(e) {this._reject(e);
}
}
_resolve(value) {
// 只有在执行环境堆栈仅蕴含平台代码时才可被调用
process.nextTick(() => {if (this._status !== PENDING) {return;}
this._status = FULFILLED;
this._value = value;
// 如果 then 的回调函数 onFulfilled, onRejected 为函数的话, 须要
// 在 promise 执行完结前其不可被调用, 当 promise 执行完结后其必须被调用
// 其调用次数不可超过一次
// 只有在执行环境堆栈仅蕴含平台代码时才可被调用
// process.nextTick(() => this._callThenCallbacks());
this._callThenCallbacks();});
}
_reject(reason) {
// 只有在执行环境堆栈仅蕴含平台代码时才可被调用
process.nextTick(() => {if (this._status !== PENDING) {return;}
this._status = REJECTED;
this._reason = reason;
// 如果 then 的回调函数 onFulfilled, onRejected 为函数的话, 须要
// 在 promise 执行完结前其不可被调用, 当 promise 执行完结后其必须被调用
// 其调用次数不可超过一次
// 只有在执行环境堆栈仅蕴含平台代码时才可被调用
// process.nextTick(() => this._callThenCallbacks());
this._callThenCallbacks();});
}
// 调用 then 回调函数队列
_callThenCallbacks() {if (_.isEmpty(this._thenCallbacks)) {return;}
this._thenCallbacks.forEach(([onFulfilled, onRejected, childResolve, childReject]) => {
try {if (this._status === FULFILLED && !onFulfilled) {
// 如果 onFulfilled 不是函数且 promise1 胜利执行,promise2 必须胜利执行并返回雷同的值
childResolve(this._value);
return;
}
if (this._status === REJECTED && !onRejected) {
// 如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回雷同的据因
childReject(this._reason);
}
let x;
if (this._status === REJECTED && onRejected) {
// 当 promise 被拒绝执行时,所有的 onRejected 需依照其注册程序顺次回调
// 其第一个参数为 promise 的拒因
// 必须被作为函数调用(即没有 this 值)x = onRejected(this._reason);
} else if (this._status === FULFILLED && onFulfilled) {
// 当 promise 胜利执行时,所有 onFulfilled 需依照其注册程序顺次回调
// 其第一个参数为 promise 的终值
// 必须被作为函数调用(即没有 this 值)x = onFulfilled(this._value);
}
// 如果 onFulfilled 或者 onRejected 返回一个值 x,则运行上面的 Promise 解决过程
this._resolvePromise(x, childResolve, childReject);
} catch (error) {childReject(error);
}
});
}
// Promise 解决过程
_resolvePromise(x, childResolve, childReject) {
// x 与 promise 相等
if (x === this) {
// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
throw new TypeError("You cannot resolve a promise with itself");
}
// 如果 x 为 Promise,则使 promise 承受 x 的状态
if (x instanceof MyPromise) {
// 如果 x 处于期待态
console.log('======PENDING===', x._status);
if (x._status === PENDING) {
// promise 需放弃为期待态直至 x 被执行或回绝
x.then(childResolve, childReject);
return;
}
// 如果 x 处于执行态
if (x._status === FULFILLED) {
// 用雷同的值执行 promise
childResolve(x._value);
return;
}
// 如果 x 处于执行态
if (x._status === REJECTED) {
// 用雷同的值执行 promise
childReject(x._reason);
return;
}
}
// x 为对象或函数
if (_.isObject(x) || _.isFunction(x)) {
// 把 x.then 赋值给 then
let then;
try {then = x.then;} catch (error) {
// 如果取 x.then 的值时抛出谬误 e,则以 e 为据因回绝 promise
// 其实这里不须要捕捉, 因为最外层有捕捉, 这里为了放弃跟标准统一
childReject(error);
return;
}
// 如果 then 是函数
if (_.isFunction(then)) {
// 将 x 作为函数的作用域 this 调用之
let called = false;
try {then.call(x, (y) => {
// 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了屡次,
// 则优先采纳首次调用并疏忽剩下的调用
if (called) {return;}
called = true;
// 如果 resolvePromise 以值 y 为参数被调用
this._resolvePromise(y, childResolve, childReject);
}, (r) => {
// 如果 resolvePromise 或 rejectPromise 曾经被调用,则疏忽之
if (called) {return;}
called = true;
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 回绝 promise
childReject(r);
});
} catch (error) {
// 如果调用 then 办法抛出了异样 e
// 如果 resolvePromise 或 rejectPromise 曾经被调用,则疏忽之
if (called) {return;}
// 否则以 e 为据因回绝 promise
childReject(error);
}
return;
}
// 如果 then 不是函数, 以 x 为参数执行 promise
childResolve(x);
return;
}
// 如果 x 不为对象或者函数, 以 x 为参数执行 promise
childResolve(x);
}
then(onFulfilled, onRejected) {
// 如果 onFulfilled 不是函数,其必须被疏忽
const _onFulfilled = _.isFunction(onFulfilled) ? onFulfilled : void 0;
// 如果 onRejected 不是函数,其必须被疏忽
const _onRejected = _.isFunction(onRejected) ? onRejected : void 0;
let childResolve;
let childReject;
const childPromise = new MyPromise((resolve, reject) => {
childResolve = resolve;
childReject = reject;
});
// then 办法能够被同一个 promise 调用屡次
this._thenCallbacks.push([_onFulfilled, _onRejected, childResolve, childReject]);
return childPromise;
}
}
module.exports = MyPromise;