源码阅读阶段
紧接上一篇,这次我们开始Promise我们最常用到的then部分的源码解析.
then()
//传入参数为两个函数,onFulfilled和onRejected
Promise.prototype.then = function(onFulfilled, onRejected) {
//判断调用者是否为Promise对象
if (this.constructor !== Promise) {
//跳转到了一个叫做safeThen的函数里面
return safeThen(this, onFulfilled, onRejected);
}
//新建一个promise对象,传入function(){}.
var res = new Promise(noop);
//handle函数,传入给promise,和一个新的Handler对象.
handle(this, new Handler(onFulfilled, onRejected, res));
//返回新的promise对象res
return res;
};
在这里我们先看看在调用者不是Promise对象时,safeThen到底做了什么.
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对象return了一个新的Promise对象.并且和then函数一样调用了handle()函数.也就是说该函数相当于new了个Promise再调用then函数一样.
handle函数
在上篇我们已经对其进行过阅读,现在可以继续看一下这一次情况有什么不同,方便阅读只放部分代码.
function handle(self, deferred) {传入一个promise对象和handler对象
//略检测代码
if (self._state === 0) {//promise对象状态为pending
if (self._deferredState === 0) {//defereds为null时,将handler对象放入defereds
self._deferredState = 1;
self._deferreds = deferred;
return;
}
if (self._deferredState === 1) {//defereds为单个时,将defereds转为数组,将handler对象放入defereds数组
self._deferredState = 2;
self._deferreds = [self._deferreds, deferred];
return;
}
//defereds为数组时,直接push添加
self._deferreds.push(deferred);
return;
}
//promise对象状态不为pending时,传入promise和handler对象,进行处理
handleResolved(self, deferred);
}
很好,接下来就是handleResolved的内部处理了,还记得上篇我们说的deferred
是一个保存了promise对象,onFulfilled函数,onRejected函数的对象,这句话么,这里我们可以得出handler对象就是我们上篇所认为的deferred.而defereds就是保存它们的地方.
handler对象
function Handler(onFulfilled, onRejected, promise){
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.promise = promise;
}
这里就很简单易懂了,handler就是这么一个东西,里面有promise对象,有onFulfilled
函数,有onRejected
函数.
中途整理
可以看到then函数其实就是将我们传入了onFulfilled
函数和onRejected
函数和新建的一个promise对象
,把三者封装到一个handler
里面.然后在handle
函数里面把handler
放进调用then函数的promise对象的_deffereds
里面.
值得注意的是
//promise对象状态不为pending时,传入promise和handler对象,进行处理
handleResolved(self, deferred);
这一段代码表示了当调用的promise部位pending状态的时候,将会对我们promise对象的_deferreds
进行处理.
结合我们上一章看到的finale()中对promise对象的_deferred
的循环handle处理,我们可以构建起整整一条关于调用的链,光说,还是不如直接说案例啦.
Promise调用then到底发生了啥?
var B = new Promise(function(resolve,reject){
console.log("construct pending");
resolve("ok");
});
B
.then(function(value){
console.log(value);
throw "ex";
},function(reason){
console.log("first");
})
.then(function(value){
console.log("second success");
console.log(value);
},function(reason){
console.log("second error");
console.log(reason);
})
.catch(function(reason){
console.log("catch error");
console.log(reason);
});
例子如上,我们逐步拆分
new Promise()
var B = new Promise(function(resolve,reject){
console.log("construct pending");
resolve("ok");
});
这里我们传入了一个函数,并console.log出了"construct pending"的字样,然后进行了resolve,参考上一篇的流程,答案显而易见,B的数据如下
A._deferredState = 0;
A._state = 1;
A._value = "ok";
A._deferreds = null;
然后我们就调用了then函数,我们跟着思路继续走.
B.then()
.then(function(value){
console.log(value);
throw "ex";
},function(reason){
console.log("first");
})
这是第一个then,我们跳到源码看一下内部做了啥,为了方便记忆,我们把传入的onFulfilled函数记为T1,onRejected函数记为T2,B也就称为B~B.then(T1,T2)
我们就对其进行探讨吧,首先在源码中先判断调用者是否为Promise对象,B.constructor!==Promise
,而这明显是Promise对象,所以不需要进行safeThen()
(虽然说safeThen也只是进行一次转换,这里不深究),然后我们就新建了一个res
变量保存new Promise(function(){})
执行 handle(this,new Handler(onFulfilled, onRejected, res));
然后返回res
跳转到调用handle()里面
传入this
为B,传入的handler
绑定了新建并应该返回的promise对象res
,还有T1,T2函数.
显现检测B是否为pending状态,结果并不是!
中间对defereds处理直接跳过.直接进行handleResolved(B,handler)
跳转到handleResolved()
检查B._state
为1也就是fulfilled
状态,返回handler.onFulfilled
给cb
变量.
跳过cb===null
的判定
执行tryCallOne(cb,B._value)
等同于
tryCallOne(function(value){
console.log(value);
throw "ex";
},"ok");
所以,可以发现console.log
除了"ok"
字串,然后在tryCallOne
中抛出了异常.根据上篇中tryCallOne
源码
//内部捕获错误,单个参数函数
function tryCallOne(fn, a) {
try {
return fn(a);
} catch (ex) {
LAST_ERROR = ex;
return IS_ERROR;
}
}
我们发现其中抛出了错误被成功捕获了并返回了标识错误IS_ERROR
.LAST_ERROR = "ok"
所以变量ret
赋值为IS_ERROR
调用reject(res,"ex")
跳转到reject()
传入参数为res
,"ex"
res._state = 2;
也就是让res状态变为rejectedres._value = "ex";
让res拒因变为"ex"
接下来的if不成立,我们不需理会.
然后调用了finale(res)
跳转到finale()
传入参数为res
由于res._deferredState = 0,所以finale不进行任何操作,至此结束handle(this,new Handler(onFulfilled, onRejected, res))
的操作,执行返回res的操作.
返回res
更改处为:
res._state = 2;
res._value = "ex";
所以传递给下一个then
的promise
对象为res
,状态为rejected
拒因为"ex"
第二个then()
调用它的promise
对象为上诉返回的res
,我们改称为resultA
resultA.then(function(value){
console.log("second success");
console.log(value);
},function(reason){
console.log("second error");
console.log(reason);
})
跟着上面的思路走,我们清楚的知道主要处理时在handle(this,new Handler(onFulfilled, onRejected, res))
的操作.
而resultA
显而易见,状态也不为pending
,直接执行handleResolved(resultA,handler)
handleResolved
中像上次一样,执行了tryCallOne()
,但是这次要注意,并没有错误被抛出,所以var ret
获取到的是函数执行后的返回值,为空.
function(reason){
console.log("second error");
console.log(reason);
}
//并没有返回任何东西
值console.log
出了"second error"和"ex"(resultA._value).
那么接下来呢,没错,ret
为空了...也就是没有错误呀
然后我们就进入了resolv(handler.promise,ret)
奇妙的跳到了resolve()
传入的ret
为空值,根据规范,这直接就跳到了
self._state = 1;//promise状态为fulfilled
self._value = ret;//值传递
finale(self);//finale了
至于finale
我们也不用理会,等于直接结束了handle()
的执行,然后返回的promise
对象我们成为resultB
,然后数据如下:
resultB._state = 1;//为fulfilled
resultB._value = null;//为空值
传递给下一个then的为fulfilled状态,终值为null
的promise
对象resultB
catch()
根据之前的博文,我们提到过catch(fn)
等同于then(undefined,fn)
主要的原因在于handleResolve
中
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;
}
那么fulfilled
状态下调用的函数自然与handler
构造中变为null
的onFulfilled
有关了.调用了resolve(deferred.promise, self._value);
传入新建的promise
和null
为终值.
新建的promise
我们称为resultC
resultC._state = 1;
resultC._value = null;
所以在catch
这段处理中
.catch(function(reason){
console.log("catch error");
console.log(reason);
})
并不会出现任何console
,因为该函数并没有被执行.catch
执行后返回promise对象为resultC,大家可以用.then(function(value){console.log(value)})
验证下,console
出来会是undefined
小结
持续进行修订吧,有时间再补上函数跳转处理图,还有Promise.race和Promise.all函数大概要等到比较久之后才会去写啦~明天开始回归继续做些小玩意
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。