Promise学习笔记:源码core.js解析-下

源码阅读阶段

紧接上一篇,这次我们开始Promise我们最常用到的then部分的源码解析.

then()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//传入参数为两个函数,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

1
2
3
4
5
6
7
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函数

在上篇我们已经对其进行过阅读,现在可以继续看一下这一次情况有什么不同,方便阅读只放部分代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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.onFulfilledcb变量.
跳过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状态变为rejected
res._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”;
所以传递给下一个thenpromise对象为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状态,终值为nullpromise对象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构造中变为nullonFulfilled有关了.调用了resolve(deferred.promise, self._value);
传入新建的promisenull为终值.
新建的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函数大概要等到比较久之后才会去写啦~明天开始回归继续做些小玩意