Promise
初始化Promise类
class Promise {
constructor(resolver) {
this[PROMISE_ID] = nextId()
this._result = this._state = undefined # 1
this._subscribers = [] # 2
if (noop !== resolver) {
typeof resolver !== 'function' && needResolver() #3
this instanceof Promise ? initializePromise(this, resolver) : needNew() #4
}
}
finally() {}
catch() {}
}
Promise.prototype.then = then
export defatult Promise
- #1 初始化Promise 包含属性result结果,state状态(pending, fullifilled, reject)三种状态不可更改
- #2 subscibers(用于异步执行时发起订阅),后面会提起
- #3 调用Promise必须需要传入参数类型为函数
- #4 调用Promise必须用构造函数方式去用new创建实例
initializePromise
初始化的时候主要就是依靠initializePromise去做一些事情
function initializePromise(promise, resolver) {
try {
resolver(function resolvePromise(value){
resolve(promise, value);
}, function rejectPromise(reason) {
reject(promise, reason);
});
} catch(e) {
reject(promise, e);
}
}
将传入的resolver执行 这个地方一开始让我很绕,其实就是一个高阶函数,执行resolver,并将resolvePromise,rejectPromise两个函数当作参数传入到resolver中供调用者手动去调用。
也就是我们调用的resolve(1)其实调用的是resovlePromise(1),resovlePromise内部又调用函数resolve。
resolve
function resolve(promise, value) {
if (promise === value) { #1
reject(promise, selfFulfillment());
} else if (objectOrFunction(value)) { #2
let then;
try {
then = value.then;
} catch (error) {
reject(promise, error);
return;
}
handleMaybeThenable(promise, value, then);
} else {
// 如果resovle的不是对象或者function直接 fulfill
fulfill(promise, value); #3
}
}
- #1 resolve调用的使用不能传入parent promise
- #2 resovle调用的时候如果传入的是对象或者函数,将会检查是否具有then属性。如果then不存在会继续走fulfill处理函数。这里不仔细讨论这种情况。
- #3 正常如果resolve传入的值不存在then属性且不为promise,将会执行fulfill函数传入value
fulfill
const PENDING = void 0 #1
const FULFILLED = 1
const REJECTED = 2
function fulfill(promise, value) {
if (promise._state !== PENDING) { return; } #2
promise._result = value;
promise._state = FULFILLED;
if (promise._subscribers.length !== 0) { #3
asap(publish, promise);
}
}
- #1 这个地方一开始很纳闷,为什么定义的是void 0 其实是有设计的 void 0 === undefined
- #2 如果当前promise状态不是初始状态,将改变result为resolve的值,state由初始值变更
- #3 如果当前promise的subscribers有订阅的消息,将会执行asap函数。
asap函数将会执行订阅的callback,其实就是针对异步的情况。resolve的执行时机决定了fulfill的调用时机。设想如果代码产生了异步执行,一段时间后resolve执行,resolve调用fulfill改变promise的state及result。在此期间new Promise().then()其实在此之前就执行了。只有.then先执行的情况下#3 条件才会成立。
then
function then(onFulfillment, onRejection) {
// parent 指的就是当前调用的实例对象promise
const parent = this;
// child 指代Promise 类
const child = new this.constructor(noop);
if (child[PROMISE_ID] === undefined) {
makePromise(child);
}
const { _state } = parent;
if (_state) { #1
// 直接返回
const callback = arguments[_state - 1];
asap(() => invokeCallback(_state, child, callback, parent._result)); # 4
} else { #2
// 异步执行
// 向parent._subscribers 中添加 child onFulfilled onRejection
subscribe(parent, child, onFulfillment, onRejection);
}
return child; # 3
}
执行then函数的时候会内部创建parent 指的就是当前调用的实例对象promise,child 指的就是Promise类。当前promise的初始状态只有上面提的fulfill执行了才会改变。
- #1 当前promise的状态不为PENDING。此时状态已变,说明同步执行。接着调用asap
- #2 当前promise的状态为PENDING。此时的状态未被改变,这个时候将会往_subscribers中添加订阅事件。fulfill再执行的时候#3条件将会成立。
- #3 这就是为什么promise可以链式调用的原因,返回新的promise实例对象
- #4 asap传入了四个值,_state就是当前promise的状态,child为新创建的实例,callback为then回调函数,最后传入当前promise的_result值
asap
let len = 0
const queue = new Array(1000);
// 检测环境
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();
}
export var asap = function asap(callback, arg) {
console.log('implement asap...')
queue[len] = callback;
queue[len + 1] = arg;
len += 2;
if (len === 2) {
// If len is 2, that means that we need to schedule an async flush.
// If additional callbacks are queued before the queue is flushed, they
// will be processed by this flush that we are scheduling.
if (customSchedulerFn) {
customSchedulerFn(flush);
} else {
scheduleFlush(); // 执行
}
}
}
asap函数往队列queue中[len]下标中添加回调函数,检测执行环境,目前我们只考虑浏览器的环境,及调用的是useMutationObserver函数
function useMutationObserver() {
let iterations = 0;
const observer = new BrowserMutationObserver(flush);
const node = document.createTextNode('');
observer.observe(node, { characterData: true });
return () => {
node.data = (iterations = ++iterations % 2);
};
}
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;
}
useMutationObserver利用了浏览器提供的MutaionObserver方法去创建一个微任务,代调用执行flush。这让我联想到了vue源码中的nextTick的实现。(vue nextTick中是优先使用promise 然后是MutationObservere 之后是Immediate 最后是setTimeout)貌似一个nextTick哈哈…执行flush后将会重置queue队列以及len,并且执行callback。这个callback被invokeCallback包了一层。
invokeCallback
function invokeCallback(settled, promise, callback, detail) {
let hasCallback = isFunction(callback),
value, error, succeeded = true;
if (hasCallback) {
try {
value = callback(detail); #1
} catch (e) {
succeeded = false;
error = e;
}
if (promise === value) {
reject(promise, cannotReturnOwn());
return;
}
} else {
value = detail;
}
// 此时的promise传入的是then函数中定义的child,及新的实例
if (promise._state !== PENDING) {
// noop
} else if (hasCallback && succeeded) {
resolve(promise, value); // 再次调用resolve
} else if (succeeded === false) {
reject(promise, error);
} else if (settled === FULFILLED) {
fulfill(promise, value);
} else if (settled === REJECTED) {
reject(promise, value);
}
}
-
#1 缓存value的值,这一步是为了获取到onFulfillment的值作为return child的_result属性。 场景:Promise().then(() => 1).then(res => console.log(res)) //1
value = callback(detail)这一步就是真正调用Promise.then(function callback () {})中callback函数的地方。
至此,then函数中的第一种同步执行情况结束。 第二种异步情况状态还未变更, 执行的subscribe
subscribe
function subscribe(parent, child, onFulfillment, onRejection) {
let { _subscribers } = parent;
let { length } = _subscribers;
parent._onerror = null;
_subscribers[length] = child; #1
_subscribers[length + FULFILLED] = onFulfillment;
_subscribers[length + REJECTED] = onRejection;
if (length === 0 && parent._state) {
asap(publish, parent);
}
}
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;
}
- #1 往parent的_subscribers中添加child,订阅事件。何时去发布呢,就是在fulfill中去检测_subscribers的length,如果存在订阅器就去执行asap(publish, promise)。 因为我前面也谈到了 fulfill调用的时机可能在then之后。
随着fulfill的调用,publish的执行会调用asap,asap又会调用publish,publish 又会调用 invokeCallback,风道轮回。其实于同步执行相比 只是多了发布订阅。
异步执行:
first then
-> subscribe
async...resolve
-> fulfill -> asap -> publish -> invokeCallback
同步执行:
resolve -> then -> asap -> invokeCallback
写了我认为主要的部分,其他的finally race all的实现我就不写了