Promise学习笔记(三):源码core.js解析(上)

开发 前端
相比大家都看到这行函数了doResolve(fn, this);,这里也就是我们初始化一个Promise时会做的事了,我们在看这个函数前,先理解下源码中类似于工具函数一样的函数.

[[171859]]

源码阅读阶段

先理解Promise根本吧,想快点理解的话可以直接跳到下个标题.这部分根据理解将持续修改。

Promise(fn) 

  1. function noop() {} 
  2. /* 
  3. 空函数,用于判断传入Promise构造器的函数是否为空函数,如果为空函数构造一个promise对象并初始化状态为pending,终值null,回调状态0和队列null
  4. */ 
  5. var LAST_ERROR = null;//记录Promise内部***的一次错误 
  6. var IS_ERROR = {}; //空对象,标识表示发生了错误 
  7. module.exports = Promise; //暴露模块接口为Promise 
  8. function Promise(fn) { 
  9.   if (typeof this !== 'object') { 
  10.     throw new TypeError('Promises must be constructed via new'); 
  11.   } 
  12.   if (typeof fn !== 'function') { 
  13.     throw new TypeError('Promise constructor\'s argument is not a function'); 
  14.   } 
  15.   this._deferredState = 0; 
  16.   this._state = 0; //promise状态 
  17.   this._value = null; //resolve返回的结果 
  18.   this._deferreds = null
  19.   if (fn === noop) return
  20.   doResolve(fn, this); //处理函数 
  21. Promise._onHandle = null
  22. Promise._onReject = null
  23. Promise._noop = noop;  

原文中表示将带有_前缀的变量在构造的时候转为(_随机数)的形式,来混淆和阻止它们被使用.接下来列出说明重要的变量。

  1. * _defferedState = 0 
  2. 表示_deferreds的类型,0时表示null,1时表示单个handler(后面讲述),2表示多个deferreds(数组) 
  3. * _state = 0 
  4. promise状态,0为pending,1为fulfilled,2为rejected,3则为值已被传递给下个promise. 
  5. * _value = null 
  6. resolve返回的结果,也就是我们所说的终值/拒因 
  7. * _deferreds = null 
  8. 表示单个或多个handler(后面讲述)  

doResolve(fn,this)

相比大家都看到这行函数了doResolve(fn, this);,这里也就是我们初始化一个Promise时会做的事了,我们在看这个函数前,先理解下源码中类似于工具函数一样的函数。

  1. //获取then方法,没有then方法标识错误 
  2. function getThen(obj) { 
  3.   try { 
  4.     return obj.then
  5.   } catch (ex) { 
  6.     LAST_ERROR = ex; 
  7.     return IS_ERROR; 
  8.   } 
  9. //内部捕获错误,单个参数函数 
  10. function tryCallOne(fn, a) { 
  11.   try { 
  12.     return fn(a); 
  13.   } catch (ex) { 
  14.     LAST_ERROR = ex; 
  15.     return IS_ERROR; 
  16.   } 
  17. //内部捕获错误,两个参数函数 
  18. function tryCallTwo(fn, a, b) { 
  19.   try { 
  20.     fn(a, b); 
  21.   } catch (ex) { 
  22.     LAST_ERROR = ex; 
  23.     return IS_ERROR; 
  24.   } 
  25.  

接下来我们直接跳到doResolve(fn,promise)处:

  1. function doResolve(fn, promise) {//传入参数为一个函数,一个promise对象 
  2.   var done = false; //保证了规范中提到的一次性 
  3.   var res = tryCallTwo(fn, function (value) {//看到了么,用于捕获错误. 
  4.     if (done) return; //这里不用说了,为了保证两个函数中只有一个函数运行且仅运行一次 
  5.     done = true
  6.     resolve(promise, value);//后续再分析它 
  7.   }, function (reason) { 
  8.     if (done) return
  9.     done = true
  10.     reject(promise, reason);//后续再分析它 
  11.   }); 
  12.   if (!done && res === IS_ERROR) { 
  13.     done = true
  14.     reject(promise, LAST_ERROR);//后续再分析它 
  15.   } 
  16.  

这就是我们的doResolve函数,可以看出,它只是个中间件,用于干什么的呢,就是解决传入函数error问题并进行reject的.重点是调用了我们很眼熟的两个函数,resolve()和reject()。

resolve() and reject()

在这个函数里我们找到了两个新东西,resolve()和reject(),看名字就知道这两个函数是什么啦,我们先看reject()吧!

  1. function reject(self, newValue) {//两个参数,从doResolve我们可以知道self是一个promise对象,而newValue就是拒因啦 
  2.   self._state = 2;//状态变成rejected了 
  3.   self._value = newValue;//promise中结果变为拒因 
  4.   if (Promise._onReject) {//在core.js中它为null,所以可能是用于别的功能.我们直接跳过 
  5.     Promise._onReject(self, newValue); 
  6.   } 
  7.   finale(self);//新的函数又出现了. 
  8.  

reject()函数传入了promise对象和一个reason拒因,函数做的就是将promise的状态变为rejected,并将promise的值进行更改.然后调用finale()函数。

可以看到出现了新函数finale(),并且传了已经进行完reject的promise对象给它.但是我们还是先看resolve()吧!

  1. function resolve(self, newValue) { 
  2. //这里写的其实就是按照规范处理的流程 
  3.   /* Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure */ 
  4.   if (newValue === self) {//传入值等于自己就抛出错误 
  5.     return reject( 
  6.       self, 
  7.       new TypeError('A promise cannot be resolved with itself.'
  8.     ); 
  9.   } 
  10.   if (//值为对象或函数 
  11.     newValue && 
  12.     (typeof newValue === 'object' || typeof newValue === 'function'
  13.   ) { 
  14.     var then = getThen(newValue);//获取值中的then函数 
  15.     if (then === IS_ERROR) {//不存在then,reject去 
  16.       return reject(self, LAST_ERROR); 
  17.     } 
  18.     if (//存在并且原来值它是一个promise对象 
  19.       then === self.then && 
  20.       newValue instanceof Promise 
  21.     ) {//同步两个promise,将传入的promise状态变为已传递并把newValue这个promise对象作为promise的值,然后finale并退出函数. 
  22.       self._state = 3; 
  23.       self._value = newValue; 
  24.       finale(self); 
  25.       return
  26.     } else if (typeof then === 'function') {//如果获取到then的值不为promise,但then是一个函数(thenable) 
  27.       doResolve(then.bind(newValue), self);//这里可以看看上个博文,对这个情况有说明,对终值自身进行doResolve取得新的值作为新的终值. 
  28.       return
  29.     } 
  30.   } 
  31.   self._state = 1;//promise状态为fulfilled 
  32.   self._value = newValue;//值传递 
  33.   finale(self);//finale了 
  34.  

在resolve()中,我们照样是传进了一个promise对象和value(终值),函数内部通过标准的判断(详细参考学习笔记(二):规范),进行[[Resolve]]操作,***将promise对象状态变更为fulfilled并改变其终值,调用finale。

finale()

我们可以进行finale()的分析了,毕竟我们的核心函数都指向它呢!

  1. function finale(self) {//传入了一个promise对象 
  2.   if (self._deferredState === 1) {//判断deferreds是单个 
  3.     handle(self, self._deferreds);//传入了promise对象和promise对象中的_deferreds 
  4.     self._deferreds = null;//让deferreds为null 
  5.   } 
  6.   if (self._deferredState === 2) {//判断deferreds是数组 
  7.     for (var i = 0; i < self._deferreds.length; i++) { 
  8.       handle(self, self._deferreds[i]);//传入了promise对象和promise对象中的_deferreds数组的所有数据 
  9.     } 
  10.     self._deferreds = null;//让deferreds为null 
  11.   } 
  12.  

很好,这下都是新的东西了,_deferredState这个就是判断_deferreds是数组还是单个的情况,并对其中每一个deferred进行handle调用.但是_defferreds又是什么呢,handle()这个函数又做了什么处理呢...

handle() 

  1. function handle(self, deferred) {//这个传入参数是预想之中的 
  2.   while (self._state === 3) {//promise状态为3的时候,也就是该promise已传递完毕的时候 
  3.     self = self._value;//重定位self为promise传递的值 
  4.   } 
  5.   if (Promise._onHandle) {//同样不属于本篇考虑范畴 
  6.     Promise._onHandle(self); 
  7.   } 
  8.   if (self._state === 0) {//promise状态为pending时 
  9.     if (self._deferredState === 0) {//没有deferreds时 
  10.       self._deferredState = 1;//deferreds变为单个 
  11.       self._deferreds = deferred;传入deferreds入列 
  12.       return
  13.     } 
  14.     if (self._deferredState === 1) { 
  15.       self._deferredState = 2;//deferreds变为数组 
  16.       self._deferreds = [self._deferreds, deferred];//传入deferred进入数组 
  17.       return
  18.     } 
  19.     self._deferreds.push(deferred);//已经是数组了就直接push增加 
  20.     return
  21.   } 
  22.   handleResolved(self, deferred);//新东西,在state!==0时传入promise和defered 
  23.  

可以看到其实这个函数在对_deferreds进行添加,进行着_deferreds的修改和写入,与finale()所做的事情恰恰相反,但是详细的处理却还是在handleResolved()函数里面。

handleResolved() 

  1. function handleResolved(self, deferred) { 
  2.   asap(function() {//注意!asap是一个引入的模块,意思就是as soon as possible,就是尽快执行的意思,我们不需要考虑它做了什么. 
  3.     // promise状态是fulfilled情况下cb为deferred中的Fulfilled函数,不是的话则为onRejected函数... 
  4.     var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected; 
  5.     if (cb === null) {//如果不存在对应状态的函数 
  6.       if (self._state === 1) {//当前promise对象是否为fulfilled状态 
  7.         resolve(deferred.promise, self._value);//传入deferred中保存的promise和当前promise的值进行resolve 
  8.       } else { 
  9.         reject(deferred.promise, self._value);//与上类似,进行reject 
  10.       } 
  11.       return
  12.     } 
  13.     var ret = tryCallOne(cb, self._value);//存在则尝试执行对应函数,返回执行结果(与两个参数的tryCall不同,这里返回了函数运行结果) 
  14.     if (ret === IS_ERROR) {//有错误,reject 
  15.       reject(deferred.promise, LAST_ERROR); 
  16.     } else {//没错误,对deferred.promise用函数返回值进行resolve 
  17.       resolve(deferred.promise, ret); 
  18.     } 
  19.   }); 
  20.  

这里我们看到了deferred是一个保存了promise对象,onFulfilled函数,onRejected函数的对象,相当于一个保存现场.其实这里就是我们即将在源码core.js解析(下)写到的handler对象了.但是这里我们暂且先不深究,知道就好了。

handleResolved()毫无疑问就是在进行着promise的终值传递处理,对旧promise对象的状态修改,并调用resolve和reject获取到值/拒因向下一个Promise传递.对这里的详细实例分析我们放到(下)来讲。

构造一个Promise时发生了啥?

从简单看起,我们构造一个Promise对象的时候经过了哪些函数。

先理一下思路。

  1. var A = Promise(function(resolve,reject){ 
  2.     resolve("ok"); 
  3. })  

这里面首先是先构造了Promise对象,我们称为A,在构造阶段执行了如下代码。

检查略.... 

  1. A._deferredState = 0; 
  2. A._state = 0; 
  3. A._value = null
  4. A._deferreds = null 

检查传入参数不为空.... 

  1. doResolve(function(resolve,reject){ 
  2.     resolve("ok"); 
  3. },this); 

 然后我们跳到了doResolve()函数处,传入为fn,promise 

  1. res = tryCallTwo(fn,function(value){resolve(promise, value);},function(reason){reject(promise, reason);}); 
  2. 出错就reject(promise,LAST_ERROR) 

出错就reject(promise,LAST_ERROR)

我们又根据我们的输入跳转到了resolve(promise,value)啦,这里理一下我们的函数,promise就是A,value其实就是我们传入的"ok"。

所以执行的是promise内部中的resolve(promise,"ok")

经过一系列判断(详细可以看规范),我们的"ok"过五关斩六将直接执行到这一步。

  1. self._state = 1;//A状态变为fulfilled 
  2. self._value = newValue;//AA终值变为"ok"  

然后我们finale(self)啦,继续跳到finale()函数,传入了A. 

  1. //在过程中我们的_deferredState始终为0呀,看了一下 
  2. A._deferredState = 0; 
  3. //已经没有什么好做的了...  

我们的构造已经结束了!这就是我们new 一个Promise时的过程,跟着思路走,其实加上reject也是同样的路线而已。

(上)结语

(下)的话我打算重点写剩余部分的then函数流程,并尽量用简单的语言来描述,希望自己能进一步理解promise,发现有错漏能及时修订。

责任编辑:庞桂玉 来源: segmentfault
相关推荐

2016-09-20 10:15:49

LaravelPHPContainer

2016-09-20 10:26:25

LaravelPHPMiddleware

2011-08-09 17:21:37

2009-06-17 14:21:39

core java

2020-07-29 17:35:08

Promise源码前端

2010-07-27 15:42:18

AdobeFlex

2010-06-12 13:08:51

UML全称

2016-12-15 09:44:31

框架Caffe源码

2010-06-13 12:49:23

UML及建模

2021-11-08 15:04:47

鸿蒙HarmonyOS应用

2010-06-28 18:44:54

UML对象图

2010-06-28 15:41:17

UML图类型

2011-09-01 14:14:00

jQuery Mobi

2012-02-23 11:06:18

JavaPlay FramewPlay!

2011-07-18 15:20:28

Core Services 框架

2022-01-10 15:30:11

鸿蒙HarmonyOS应用

2011-08-31 15:59:10

LUAWeb 开发

2010-07-12 14:32:29

UML对象关系

2011-08-23 17:33:08

LuaMetatable

2010-06-28 18:36:06

UML协作图
点赞
收藏

51CTO技术栈公众号