1. PROMISE_ID
当创建 Promise
的时候就会生成一个 PROMISE_ID
,目前还不知道是干啥的,生成的规则:
export const PROMISE_ID = Math.random().toString(36).substring(2);
2. Promise 的成员变量
当创建 Promise
对象的时候会初始化三个变量。
- _result 保存
Promise
的结果信息; - _state 保存
Promise
的状态信息; - _subscribers 保存
Promise
的相关回调。
2.1 _result
保存 Promise
执行的结果。
2.2 _state
Promise
里面有三个状态:
- PENDING 代表正在执行中;
- FULFILLED 已经成功执行;
- REJECTED 执行完成,但是出现了错误。
这三个所对应的真实值分别为:PENDING(void 0)
;FULFILLED(1)
;REJECTED(2)
。整个 Promise 执行的过程中会经历,当没有出现错误的情况( PENDING -> FULFILLED
);当出现错误的情况( PENDING -> REJECTED
)。
2.3 _subscribers
这里面保存的是要执行的函数,也就是 .then
传递的回调函数,函数声明:
declare function then(onFulfillment: (result: any) => any, onRejection: (error: any) => any): Promise
只不过除了上面的两个函数外,还有一个空的函数,源码中写的是 noop
:
function noop() {}
当这些函数保存以后得到的 _subscribers
数组为:
[ noop, onFulfillment, onRejection]
3. Promise 的初始化
constructor(resolver) {
this[PROMISE_ID] = nextId();
this._result = this._state = undefined;
this._subscribers = [];
if (noop !== resolver) {
typeof resolver !== 'function' && needsResolver();
this instanceof Promise ? initializePromise(this, resolver) : needsNew();
}
}
可以看到主要是初始化成员变量;校验传入的参数 resolver
除此之外的重点就是 initializePromise
了。
既然说到这里了,就根据代码说说传入参数的说明:
传入的 resolver
必须是一个函数,且不能是 noop
函数;回调不能是 Promise
。我们知道有时候我们有在回调里面调用异步方法的,这个时候我们不应该尝试传入异步的回调方法,而应该是在回调里面通过下面的方式调用异步方法:
// 错误的做法
new Promise(new Promise((resolve, reject) => {
// 做相关的逻辑
}))
// 正确的做法
new Promise((resolve, reject) => {
new Promise((resolve, reject) => {
// 做相关逻辑
}).then((value) => {
// 这里拿到值做相关逻辑
})
})
4. Promise 的任务队列
在这个源码里面有任务队列的,专门负责执行 Promise
里面的操作。初始化的地方:
const queue = new Array(1000);
每个任务占两个元素的位置:
- 要执行的任务;
- 当前执行任务的
Promise
下面我们来看看消费任务队列的函数:
function flush() {
for (let i = 0; i < len; i+=2) {
let callback = queue[i];
let arg = queue[i+1];
callback(arg);
queue[i] = undefined;
queue[i+1] = undefined;
}
len = 0;
}
记住每次循环拿到的 callback
和 arg
,下面会详细讲到。我们看到就是循环上面的队列,等全部完成后,重置队列的下标 len = 0
。至于是什么负责执行这个消费函数,具体要看你的执行函数,如果是 nodejs
,那么就使用:
process.nextTick(flush)
这里的 flush
就是上面的 flush 函数,非常的直观和清晰。至于其他的,对应的代码如下:
let scheduleFlush;
// Decide what async method to use to triggering processing of queued callbacks:
if (isNode) {
scheduleFlush = useNextTick();
} else if (BrowserMutationObserver) {
scheduleFlush = useMutationObserver();
} else if (isWorker) {
scheduleFlush = useMessageChannel();
} else if (browserWindow === undefined && typeof require === 'function') {
scheduleFlush = attemptVertx();
} else {
scheduleFlush = useSetTimeout();
}
现在我们再回过头看一下队列里面的数据到底是怎样保存的;首先我们先看队列中的 callback
里面是啥?
function publish(promise) {
let subscribers = promise._subscribers;
let settled = promise._state;
if (subscribers.length === 0) { return; }
let child, callback, detail = promise._result;
for (let i = 0; i < subscribers.length; i += 3) {
child = subscribers[i];
callback = subscribers[i + settled];
if (child) {
invokeCallback(settled, child, callback, detail);
} else {
callback(detail);
}
}
promise._subscribers.length = 0;
}
publish
函数的参数就是 Promise
本身,只不过在这里由于不是 Promise
类的一部分,所以需要传递过来,而这个参数也就是队列里面的 arg
。可以看到函数就是将 Promise
中保存的 _subscribers
消费掉;消费原则就是根据 _state
属性;这里为啥会有循环呢??这个结合后面我们调用实例的时候会具体说,这里只说明一下,是因为使用者可能会有多个 .then
方法,所以存在循环。下面我们看每次循环拿到的 child
和 callback
这两个变量。结合上面我们说的 _subscribers
保存的值:
[child, onFulfillment, onRejection, child, onFulfillment, onRejection, ...]
其中这里的 callback 函数取决于 _state 的值,然后它的取值分别是 undefined,1,2
,如果此时 Promise 的状态是 1
也就是 FULFILLED
,那么取到到的值就是 onFulfillment
;如果是 2
,那么取到的值则为: onRejection
。
5. 实例化 Promise
const delay1000 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('------delay1000')
resolve()
}, 1000)
})
这个时候什么都不操作仍然能打印上面的日志,这是因为当你调用 new Promise
的时候就已经开始执行你传入的 resolver
回调函数了。
function initializePromise(promise, resolver) {
try {
resolver(function resolvePromise(value){
resolve(promise, value);
}, function rejectPromise(reason) {
reject(promise, reason);
});
} catch(e) {
reject(promise, e);
}
}
这个函数就是在 Promise
构造函数里面执行的,而 Promise
回调给我们的就是:
// resolve
function resolvePromise(value){
resolve(promise, value);
}
// reject
function rejectPromise(reason) {
reject(promise, reason);
}
所以当我们调用 resolve
和 reject
的时候实际上是调用里面的 resolve(promise, value)
或 reject(promise, reason)
。而这两个函数的目的就是将结果数据保存起来,我们首先看 resolve
:
function resolve(promise, value) {
// 如果返回的值是本身,那么直接调用 reject
if (promise === value) {
reject(promise, selfFulfillment());
// 如果是对象或者方法
} else if (objectOrFunction(value)) {
let then;
try {
then = value.then;
} catch (error) {
reject(promise, error);
return;
}
handleMaybeThenable(promise, value, then);
// 如果只是普通的值,那么就走这里,像上面的例子就走这里
} else {
fulfill(promise, value);
}
}
上面我们传入的值是 undefined
,也就是 value
是 undefined
。然后我们接着看 fulfill
函数:
function fulfill(promise, value) {
if (promise._state !== PENDING) { return; }
promise._result = value;
promise._state = FULFILLED;
if (promise._subscribers.length !== 0) {
asap(publish, promise);
}
}
当我们只是创建 Promise
的时候,也就是还没有执行 .then
方法,那么 _state
的值永远都是 PENDING
;所以我们上面的示例执行到这里的时候这个条件是不满足的,就会执行到下面的代码,也就是保存执行的结果和更新当前 Promise
状态,也就是从 PENDING -> FULFILLED
;而 promise._subscribers
的长度对于我们的示例来说就是 0
。 asap
这个函数就是将任务加入到队列中,但此时不会执行。
6. .then 操作
Promise
的结果正常是通过 .then
来拿到执行的结果。
function then(onFulfillment, onRejection) {
const parent = this;
const child = new this.constructor(noop);
if (child[PROMISE_ID] === undefined) {
makePromise(child);
}
const { _state } = parent;
// 如果在执行 Promise 的时候状态是 FULFILLED 或 REJECTED
if (_state) {
const callback = arguments[_state - 1];
asap(() => invokeCallback(_state, child, callback, parent._result));
} else {
// 如果状态是 PENDING ,那么就把相关参数保存起来
subscribe(parent, child, onFulfillment, onRejection);
}
return child;
}
看到这里逻辑就清晰起来了。下面来看一下流程图(不是很规范),其中循环队列,其实就是循环上面说的数组。
还有一个流程没有画进去,也就是当 .then
方法返回的还是 Promise
的时候,我们先来看看代码:
function handleMaybeThenable(promise, maybeThenable, then) {
// 如果返回的是 Promise 并且是这个 Promise
if (maybeThenable.constructor === promise.constructor &&
then === originalThen &&
maybeThenable.constructor.resolve === originalResolve) {
handleOwnThenable(promise, maybeThenable);
} else {
if (then === undefined) {
fulfill(promise, maybeThenable);
// 如果是一个方法,也就是当这个是别人开发的 Promise
} else if (isFunction(then)) {
handleForeignThenable(promise, maybeThenable, then);
} else {
fulfill(promise, maybeThenable);
}
}
}
从这个代码我们知道,只要是 Promise
,那么是可以相互之间使用的,比如你即使用了这个库的 Promise
,也使用了其他库的,这样是可以的。下面先看如果仍然返回的是这个库的 Promise
,那么逻辑怎么处理:
function handleOwnThenable(promise, thenable) {
if (thenable._state === FULFILLED) {
fulfill(promise, thenable._result);
} else if (thenable._state === REJECTED) {
reject(promise, thenable._result);
} else {
subscribe(thenable, undefined, value => resolve(promise, value),
reason => reject(promise, reason))
}
}
如果状态是完成状态( FULFILLED
和 REJECTED
),那么就直接返回,否则就保存起来。如果是用别人开发的 Promise
,那么就这样处理逻辑:
function handleForeignThenable(promise, thenable, then) {
asap(promise => {
let sealed = false;
let error = tryThen(then, thenable, value => {
if (sealed) { return; }
sealed = true;
if (thenable !== value) {
resolve(promise, value);
} else {
fulfill(promise, value);
}
}, reason => {
if (sealed) { return; }
sealed = true;
reject(promise, reason);
}, 'Settle: ' + (promise._label || ' unknown promise'));
if (!sealed && error) {
sealed = true;
reject(promise, error);
}
}, promise);
}
使用的时候我们知道,我们返回的 Promise
,是将结果给下一个 .then
的回调,而 .then 的回调是保存在 _subscribers
参数中,那么这里要做的就是将其他库中的结果去消化我们库的 _subscribers
。所以可以看到传入的回调中执行了 resolve
,而这个函数就会去消化 _subscribers
。其中 tryThen 就是执行其他库的 .then 方法:
function tryThen(then, value, fulfillmentHandler, rejectionHandler) {
try {
then.call(value, fulfillmentHandler, rejectionHandler);
} catch(e) {
return e;
}
}
7. all
all
会等传入的全部的 Promise
都执行完成了才返回。
// a, b, c, d Promise 的实例化
Promise.all([a, b, c, d]).then(_result => {
// 要等到 a, b, c, d 这四个异步任务都完成才走到这里
})
下面我们看源码,先看实例化方法:
export default function all(entries) {
return new Enumerator(this, entries).promise;
}
就是去实例化 Enumerator
类,然后我们接着看 Enumerator
类的构造方法。
constructor(Constructor, input) {
this._instanceConstructor = Constructor;
// 给自己实例化一个 Promise 作为自己的局部变量
// 也就是把整个 all 当成是一个 Promise,这样就可以整体返回了
this.promise = new Constructor(noop);
if (!this.promise[PROMISE_ID]) {
makePromise(this.promise);
}
if (isArray(input)) {
// 总的长度
this.length = input.length;
// 剩余长度,没有执行完成的 Promise 长度
this._remaining = input.length;
// 保存传入数组的 Promise 执行结果
this._result = new Array(this.length);
if (this.length === 0) {
// 如果是空数组,那么就直接返回
fulfill(this.promise, this._result);
} else {
this.length = this.length || 0;
this._enumerate(input);
// <1>如果剩余量都空了,那就代表执行完成了,直接返回
if (this._remaining === 0) {
fulfill(this.promise, this._result);
}
}
} else {
// 如果传入得到不是数组,那么直接报错
reject(this.promise, validationError());
}
}
其中 <1>
这个地方为啥会执行完成呢,上面 Promise 的代码如果看明白的话,我们知道 Promise
实例化的时候就已经开始执行 Promise
了,如果当执行 all
的时候完全有可能全部都执行完成了。不管有没有执行完成这里我们都得看 _enumerate
这个函数,因为 _remaining
的长度值是在这里面减的。
_enumerate(input) {
for (let i = 0; this._state === PENDING && i < input.length; i++) {
this._eachEntry(input[i], i);
}
}
这个函数似乎很简单,就是循环 Promise
数组。所以还是具体看 _eachEntry
函数。
_eachEntry(entry, i) {
let c = this._instanceConstructor;
let { resolve } = c;
// 这是判断 Promise 的 resolve 是不是这个库自己的 resolve
if (resolve === originalResolve) {
let then;
let error;
let didError = false;
try {
// 看一下 then 方法
then = entry.then;
} catch (e) {
didError = true;
error = e;
}
// then 方法是不是这个库自己的 then 方法
if (then === originalThen &&
entry._state !== PENDING) {
// 当状态已经完成,执行这个
this._settledAt(entry._state, i, entry._result);
} else if (typeof then !== 'function') {
// 如果 then 不是方法,那么直接把传入的值当做结果,同时维护 _remaining 变量
this._remaining--;
this._result[i] = entry;
} else if (c === Promise) {
let promise = new c(noop);
if (didError) {
reject(promise, error);
} else {
handleMaybeThenable(promise, entry, then);
}
this._willSettleAt(promise, i);
} else {
this._willSettleAt(new c(resolve => resolve(entry)), i);
}
} else {
this._willSettleAt(resolve(entry), i);
}
}
我们首先看一下,当执行这个函数状态就完成的情况,也就是会执行 _settledAt
方法。
_settledAt(state, i, value) {
let { promise } = this;
if (promise._state === PENDING) {
// 维护剩余长度 _remaining
this._remaining--;
if (state === REJECTED) {
// 如果是错误抛出异常
reject(promise, value);
} else {
// 如果是正确的结果就直接保存
this._result[i] = value;
}
}
// 如果剩余长度为 0 ,那整体完成
if (this._remaining === 0) {
fulfill(promise, this._result);
}
}
就是保存一下结果,同时看一下是不是 all
都完成了,如果完成了就整体都完成,否则就执行其他的。
8. resolve 和 reject
这个比较简单,就是将传入的值转换成 Promise
,如果本身就是 Promise
,那么就保持原样。
function resolve(object) {
/*jshint validthis:true */
let Constructor = this;
if (object && typeof object === 'object' && object.constructor === Constructor) {
return object;
}
let promise = new Constructor(noop);
_resolve(promise, object);
return promise;
}
_resolve
这个函数上面说过,其实就是判断传入的值是啥,如果是值就直接保存起来,如果是 Promise
就保存 .then
的回调。这里的值都是保存到 resolve
回调中; reject 则是保存到 reject
回调中。
function reject(reason) {
/*jshint validthis:true */
let Constructor = this;
let promise = new Constructor(noop);
_reject(promise, reason);
return promise;
}
9. race
这个我们知道传入的 Promise
或者值中,谁先完成就返回谁。
function race(entries) {
/*jshint validthis:true */
let Constructor = this;
if (!isArray(entries)) {
return new Constructor((_, reject) => reject(new TypeError('You must pass an array to race.')));
} else {
return new Constructor((resolve, reject) => {
let length = entries.length;
for (let i = 0; i < length; i++) {
Constructor.resolve(entries[i]).then(resolve, reject);
}
});
}
}
看源码很清楚就是把传入的数组通过 Promise.resolve
转换成 Promise
,然后直接传入回调,而且是同一个,所以谁先完成这个 Promise
就完成。