资讯专栏INFORMATION COLUMN

Promise学习笔记(四):源码core.js解析(下)

VincentFF / 2665人阅读

摘要:源码阅读阶段紧接上一篇这次我们开始我们最常用到的部分的源码解析传入参数为两个函数和判断调用者是否为对象跳转到了一个叫做的函数里面新建一个对象传入函数传入给和一个新的对象返回新的对象在这里我们先看看在调用者不是对象时到底做了什么比想象的要简单

源码阅读阶段

紧接上一篇,这次我们开始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.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函数大概要等到比较久之后才会去写啦~明天开始回归继续做些小玩意

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80387.html

相关文章

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

    摘要:源码阅读阶段先理解根本吧想快点理解的话可以直接跳到下个标题这部分根据理解将持续修改空函数用于判断传入构造器的函数是否为空函数如果为空函数构造一个对象并初始化状态为终值回调状态和队列记录内部最后的一次错误空对象标识表示发生了错误暴露模块接口为 源码阅读阶段 先理解Promise根本吧,想快点理解的话可以直接跳到下个标题.这部分根据理解将持续修改. Promise(fn) function...

    wuyangchun 评论0 收藏0
  • JS笔记

    摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。异步编程入门的全称是前端经典面试题从输入到页面加载发生了什么这是一篇开发的科普类文章,涉及到优化等多个方面。 TypeScript 入门教程 从 JavaScript 程序员的角度总结思考,循序渐进的理解 TypeScript。 网络基础知识之 HTTP 协议 详细介绍 HTT...

    rottengeek 评论0 收藏0
  • 嗨,了解一,我的Promise学习笔记

    摘要:回调函数成功回调处理器失败回调处理器用户发送一个向百度服务器获取数据的异步请求无阻塞高并发的的诞生更加严重的依赖异步操作才能完成无阻赛高并发的特性。 Promise Promise 是什么? 词语本意: 发音:[ˈprɒmɪs] 词性:名词, 翻译:许诺,允诺。 MDN解释 Promise 对象用于一个异步操作。 一个Promise表示一个现在,将来或永不可能可用的值。 按照书写方...

    yanest 评论0 收藏0
  • 日常笔记

    摘要:零延迟零延迟并不是意味着回调函数立刻执行。异步编程的中方法包括回调函数事件监听采用事件驱动模式。执行完成后,立即触发事件,从而开始执行。所谓对象,就是代表了未来某个将要发生的事件通常是一个异步操作。 JavaScript单线程与浏览器多线程 Javascript是单线程的:因为JS运行在浏览器中,是单线程的,每个window一个JS线程。作为浏览器脚本语言,JavaScript的主要...

    myshell 评论0 收藏0
  • Promise学习笔记(二):规范

    摘要:下一篇大概就是源码方面的学习笔记了龟速学习中这一次我是去看了下规范照例传送门图灵社区规范首先吧个人总结下该用的词解决结婚拒绝婉拒终值值传家宝拒因好人卡等等异常车祸理下概念我们的的就像是一场姻缘对吧解决呢就是结婚成功啦传家宝也如愿的传给下一代 下一篇大概就是源码方面的学习笔记了...龟速学习中... 这一次我是去看了下Promises/A+规范照例传送门:图灵社区Promises/A+规...

    _Suqin 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<