网站设置屏蔽广告,长沙专业网站设计,金华网站建设大型网页建设,WordPress允许用户删除评论Promise源码解析
纸上得来终觉浅#xff0c;绝知此事要躬行。之前只是很浅显的知道Promise的用法#xff0c;也大概猜测到它的内部是如何实现的。但是总是有一种不深究一下就不踏实的感觉。于是从npm上获得早期的Promise源代码#xff0c;拿过来读一读#xff0c;做一做笔…Promise源码解析
纸上得来终觉浅绝知此事要躬行。之前只是很浅显的知道Promise的用法也大概猜测到它的内部是如何实现的。但是总是有一种不深究一下就不踏实的感觉。于是从npm上获得早期的Promise源代码拿过来读一读做一做笔记。
Promise的源码写的非常的巧妙凭空阅读会陷入入其中无法自拔。
简单的Promise用法
一个简单的Promise用法如下
const promiseObj new Promise(function (resolve, reject) {// 代码执行resolve(value);//orreject(error);
});
promiseObj.then(function (value) {// do sth...
}, function (error) {// deal excption...
});
promiseObj.catch(function (error) { // catch excption...
});我们就从这个简单的例子开始分析Promise的执行是怎么样的。 Promise的源代码位于https://github.com/stefanpenner/es6-promise.git ,这里采用的版本为0.1.0版本。 我们从Promise的构造函数开始说起
function Promise(resolver) {... // 省略的部分为必要的校验this._subscribers [];invokeResolver(resolver, this);
}_subscribers对象用于存储这个Promise实例所对应的观察者。紧接着执行invokeResolver();
function invokeResolver(resolver, promise) {function resolvePromise(value) {resolve(promise, value); // p2, p1}function rejectPromise(reason) {reject(promise, reason);}try {resolver(resolvePromise, rejectPromise);} catch(e) {rejectPromise(e);}
}invokeResolver为关键的一环。在这里构造Promise对象时所传入的方法会被执行并将执行方法所需的两个回调参数resolvePromise和rejectPromise传了进去方便业务代码通知Promise对象执行结果。
如果我们的代码很简单的执行了一行代码例如 resolve(Hello);那么resolvePromise会紧接着调用resolve方法。我们进入resolve方法一探究竟
function resolve(promise, value) { // promise为新构造的Promise对象value Hello.if (promise value) {fulfill(promise, value);} else if (!handleThenable(promise, value)) {fulfill(promise, value);}
}我们现在的逻辑会直接进入fulfill方法
function fulfill(promise, value) {// promise为新构造的Promise对象value Hello.if (promise._state ! PENDING) { return; } // 当前不满足默认为PENDING状态promise._state SEALED;// promise._state SEALED promise._detail value;// promise._detail Hello config.async(publishFulfillment, promise);
}到这里将结果值赋值给了Promise对象。也就是说Promise对象保留了计算后的结果值’Hello’。然后我们看一下config.async()是个什么鬼
function asap(callback, arg) {var length queue.push([callback, arg]);if (length 1) {// If length is 1, 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.scheduleFlush();}
}上面这段代码位于lib/promise/asap.js中。它主要用来将任务callback加入下一个时间片中。执行到这里会将[publishFulfillment, promise]组成一个数组放在队列中然后紧接着根据平台进行任务刷新也就是执行scheduleFlush();
不过不管scheduleFlush方法再怎么快它也是被放在了所有事件最后才执行。所以接下来执行的代码是主线程继续执行的代码
promiseObj.then(function (value) {// do sth...
}, function (error) {// deal excption...
});
promiseObj.catch(function (error) { // catch excption...
});到这里我们需要看看Promise.then方法是怎么执行的 then: function(onFulfillment, onRejection) {var promise this;var thenPromise new this.constructor(function() {});if (this._state) {var callbacks arguments;config.async(function invokePromiseCallback() {invokeCallback(promise._state, thenPromise, callbacks[promise._state - 1], promise._detail);});} else {subscribe(this, thenPromise, onFulfillment, onRejection);}return thenPromise;},在调用then方法时产生了一个新的Promise对象thenPromise它会参与后面的任务执行。现在我们根据上下文进入subscribe方法
function subscribe(parent, child, onFulfillment, onRejection) { // parent customPromiseObject, child thenPromise, onFulfillment 成功回调onRejection 失败回调var subscribers parent._subscribers;var length subscribers.length;subscribers[length] child;subscribers[length FULFILLED] onFulfillment;subscribers[length REJECTED] onRejection;
}subscribers初始化是一个空数组所以在这里执行完毕后将会是以下效果 subscribers[0] thenPromise;subscribers[1] 成功回调; subscribers[2] 失败回调;promise._subscribers subscribers;由于我们的示例代码使用了catch所以贴一下catch方法的源代码 catch: function(onRejection) {return this.then(null, onRejection);}所以在执行完then方法以及catch方法之后promise._ subscribers内部如下 subscribers[0] thenPromise;subscribers[1] 成功回调; subscribers[2] 失败回调; subscribers[3] thenPromise2;subscribers[4] null; subscribers[5] catch回调;到这里我们的同步事件就执行完了。接下来开始分析异步事件。我们回到lib/promise/asap.js文件。
在Promise早期的源代码中对Promise的运行平台做了区分。我们这里对这块细节不做深究但不管是哪个平台最终还是会执行到flush方法。这里需要注意在执行flush方法时它已经在所有事件执行之后了。这里的所有事件指的是已经进入主线程队列的事件也就是刚刚执行完成的同步事件。
function flush() {for (var i 0; i queue.length; i) {var tuple queue[i];var callback tuple[0], arg tuple[1];callback(arg);}queue [];
}flush方法在这里会回调刚刚传入的方法publishFulfillment参数为那个promise对象。我们确认一下publishFulfillment方法的细节
function publishFulfillment(promise) {publish(promise, promise._state FULFILLED);
}它这里很简单直接调用了publish方法。不过在这里promise对象的状态再一次被改变了。从PENDING - SEALED - FULFILLED。我们进入publish
function publish(promise, settled) { // promise new Promise, settled FULFILLEDvar child, callback, subscribers promise._subscribers, detail promise._detail;for (var i 0; i subscribers.length; i 3) { child subscribers[i];callback subscribers[i settled]; invokeCallback(settled, child, callback, detail); }promise._subscribers null;
}上面的代码开始Promise对象的观察者进行通知。注意这里的for循环只循环了两次。第一次循环 child thenPromise;callback 成功回调;我们先来看正常的执行这里直接调用了invokeCallback方法
function invokeCallback(settled, promise, callback, detail) { settled FULFILLED, promise thenPromise, callback 成功回调, detail Hello// 检测callback是否是一个方法var hasCallback isFunction(callback),value, error, succeeded, failed;// 如果是方法则执行if (hasCallback) {try {value callback(detail); succeeded true;} catch(e) {failed true;error e;}} else {value detail;succeeded true;}if (handleThenable(promise, value)) {return;} else if (hasCallback succeeded) {resolve(promise, value);} else if (failed) {reject(promise, error);} else if (settled FULFILLED) {resolve(promise, value);} else if (settled REJECTED) {reject(promise, value);}
}咱们的示例比较简单在检测回调方法是一个方法之后就直接调用了。我们的示例也没有返回值。所以直接走进了hasCallback succeeded的判断中然后进入resolve方法。
function resolve(promise, value) { // promise thenPromise, value undefinedif (promise value) {fulfill(promise, value);} else if (!handleThenable(promise, value)) {fulfill(promise, value);}
}根据上下文这里进入了fulfill方法:
function fulfill(promise, value) {// promise thenPromise, value null if (promise._state ! PENDING) { return; }promise._state SEALED;// thenPromise._state SEALED promise._detail value;// thenPromise._detail null config.async(publishFulfillment, promise);
}到这里是不是似曾相识没错我们在上面已经见过上面两个方法。不过在这里是要通过flush回调执行thenPromise对象。然后根据上面提到的逻辑最后thenPromise将会进入publish:
function publish(promise, settled) { // promise thenPromise, settled FULFILLED var child, callback, subscribers promise._subscribers, detail promise._detail;for (var i 0; i subscribers.length; i 3) { child subscribers[i];callback subscribers[i settled]; invokeCallback(settled, child, callback, detail); }promise._subscribers null;
}不过thenPromise对象是一个空对象它的_subscribers属性是一个空数组。所以这里自然执行完毕。
不过到这里也才是thenPromise执行完毕我们自己构造的Promise对象呢它才准备进行第二次for循环 child thenPromise2;callback null;然后根据上下文在执行到invokeCallback方法不过最终它的命运匹配到了settled FULFILLED的条件进入了resolve方法
function resolve(promise, value) { // promise new Promise, value undefinedif (promise value) {fulfill(promise, value);} else if (!handleThenable(promise, value)) {fulfill(promise, value);}
}再进入fulfill方法:
function fulfill(promise, value) {if (promise._state ! PENDING) { return; }promise._state SEALED;promise._detail value;config.async(publishFulfillment, promise);
}不过执行到这里promise的_state已经为SEALED了不等于PENDING所以Promise也就自然终结了。
至此一个普通的Promise执行终结。