首页 > 编程语言 >JavaScript Promise超详细源码解读

JavaScript Promise超详细源码解读

时间:2024-01-08 21:37:33浏览次数:44  
标签:resolve self JavaScript value state 源码 Promise ._

Promise超详细源码解读

说到promise,相信大家在日常开发中都经常使用到,它是我们异步操作中必不可少的一部分,可以让代码看起来变得更好理解;

我曾在技术社区看过许多关于promise底层原理的文章,大概原理明白,这次,我准备系统的分析实现源码并记录下来,本文将一行行代码去分析最后附加流程图和总结,希望这能对你有帮助;

promise的实现库有这么多,接下来我们以github的promise ployfill为例,想看完整实现代码的在这里

最基础用法

我们先想象一下promise最常见的使用方式,大概像这样

new Promise((resolve,reject) => {
    setTimeout( () => resolve('result'),1000)
}).then( (value) => {
    console.log( value )
})

首先是new Promise(),那么Promise肯定是一个定好的构造函数,果然。

Promise Func

function Promise(fn) {
  if (!(this instanceof Promise))
    throw new TypeError('Promises must be constructed via new');
  if (typeof fn !== 'function') throw new TypeError('not a function');
  this._state = 0;
  this._handled = false;
  this._value = undefined;
  this._deferreds = [];
  doResolve(fn, this);
}
  1. 可以看到这里大致限制了必须要使用new操作符调用Promise,还有我们的参数必须是函数类型的;
  2. 初始化属性(_state,__handled,_value,_deferreds),这些属性后面要用到;
  1. 先看一下_state
/** 内部状态码
   * 0: pending,当前Promise正在执行中,默认值
   * 1: fulfilled, 执行了resolve函数,且参数_value不是期约,即_value instanceof Promise === false
   * 2: rejected,执行了reject函数
   * 3: fulfilled,执行了resolve函数,且参数_value是期约,即_value instanceof Promise === true
*/
  1. _value为存放我们最终resolve或reject的结果
  2. _deferreds缓存then方法执行后生成的handle实例对象,把传入的回调缓存成handle实例对象的属性
  1. 调用doResolve(fn, this);

可以看到我们把fn和this传递给了doResolve执行,接着看看doResolve;

doResolve Func

function doResolve(fn, self) {
  var done = false;
  try {
    fn(
      function(value) {
        if (done) return;
        done = true;
        resolve(self, value);
      },
      function(reason) {
        if (done) return;
        done = true;
        reject(self, reason);
      }
    );
  } catch (ex) {
    if (done) return;
    done = true;
    reject(self, ex);
  }
}

可以看到doResolve很简单,它直接调用了我们new Promise(fn)传递进去的fn,把两个匿名函数传递给了resolve,reject给我们在外部操作完成后调用,然后如果有出错会直接帮我们调用reject;

// 可以看回我们new Promise(fn)传递进去的fn
(resolve,reject) => {
    setTimeout( () => resolve('result'),1000)
}

执行fn,我们的setTimeout在1000ms后调用了resolve('result');

传递给fn的resolve是

function(value) {
	if (done) return;
	done = true;
	resolve(self, value);
}

这里的done实际上是为了防止我们多次调用resolve,只有第一次生效吧;

由于我们是异步调用resolve的,执行完setTimeout之后就会执行then,1000ms后才会调用resolve [我们往then Func里面看](#then Func) 由于我们fn里只调用了resolve,接下来我们直接看resolve

resolve Func

function resolve(self, newValue) {
  try {
    if (newValue === self)
      throw new TypeError('A promise cannot be resolved with itself.');
    if (
      newValue &&
      (typeof newValue === 'object' || typeof newValue === 'function')
    ) {
      var then = newValue.then;
      if (newValue instanceof Promise) {
        self._state = 3;
        self._value = newValue;
        finale(self);
        return;
      } else if (typeof then === 'function') {
        doResolve(bind(then, newValue), self);
        return;
      }
    }
    self._state = 1;
    self._value = newValue;
    finale(self);
  } catch (e) {
    reject(self, e);
  }
}

resolve主要是校验我们newValue的类型,我们的newValue是'result',自然是string

  1. self是通过Promise Func -> doResolve Func,传递下去的this,也就是首先判断的是newValue不能是new Promise的this本身
  2. newValue是object || function类型
  1. newValue是Promise实例,像我们上面说的_state会是3(),_把newValue赋值给我们最终resolve的结果,并执行finale(self);
  2. newValue.then是函数,会重新执行doResolve,不会执行finale;
  1. newValue不是Promise实例,newValue.then也不是函数,_state会是1(),把newValue赋值给我们最终resolve的结果,并执行finale(self);

很显然,我们的例子里resolve的类型效验会执行以上list->3,我们看看finale

finale Func

function finale(self) {
  if (self._state === 2 && self._deferreds.length === 0) {
    Promise._immediateFn(function() {
      if (!self._handled) {
        Promise._unhandledRejectionFn(self._value);
      }
    });
  }

  for (var i = 0, len = self._deferreds.length; i < len; i++) {
    handle(self, self._deferreds[i]);
  }
  self._deferreds = null;
}

_state为1,finale代码会执行

for (var i = 0, len = self._deferreds.length; i < len; i++) {
    handle(self, self._deferreds[i]);
}
self._deferreds = null;

标签:resolve,self,JavaScript,value,state,源码,Promise,._
From: https://blog.51cto.com/u_15590807/9150482

相关文章

  • 【设计模式】建造者模式——建造者模式在Android SDK源码中的应用
    建造者模式在AndroidSDK源码中也有广泛的应用,本文挑两个典型的类讨论一下:AlertDialog.Builder在Android源码中最常用到的建造者模式非AlertDialog.Builder莫属,代码如下:AlertDialogalertDialog=newAlertDialog.Builder(mContext) .setTitle("系统提示:").setMessage("请......
  • 百度地图JavaScript API v2.0创建地图
    接口文档:https://lbsyun.baidu.com/index.php?title=jspopular3.0https://lbs.baidu.com/faq/api?title=webapi地图创建代码:<!DOCTYPEhtml><html><head> <metahttp-equiv="Content-Type"content="text/html;charset=utf-8"/> &......
  • 前端三剑客——HTML5+CSS3+JavaScript
    核心技术●实战训练营●项目实战(微视频版)  《前端三剑客——HTML5+CSS3+JavaScript》采用“核心技术→实战训练营→企业级项目实践”的结构和“由浅入深,由深到精”的模式进行讲解。  全书科学设置七大阶段由浅入深循序渐进,为解决实际问题而生。第一阶段、第三阶段、第五阶段......
  • Spring MVC 源码分析 - RequestToViewNameTranslator 组件
    RequestToViewNameTranslator组件RequestToViewNameTranslator 组件,视图名称转换器,用于解析出请求的默认视图名。就是说当ModelAndView对象不为 null,但是它的View对象为 null,则需要通过 RequestToViewNameTranslator 组件根据请求解析出一个默认的视图名称。回顾先来回顾......
  • JavaScript WebAPI(三)(详解)
    这次介绍一下webAPI中的一些知识:回调函数回调函数是指如果将函数A做为参数传递给函数B时,我们称函数A为回调函数例如://立即执行函数中传递的函数是一个回调函数(function(){console.log("我是回调函数")})();//监听事件中传递的参数是一个回调函数constdiv=document......
  • JavaScript WebApi(二) 详解
    监听事件介绍事件监听是一种用于在特定条件下执行代码的编程技术。在Web开发中,事件监听器可以用于捕获和响应用户与页面交互的各种操作,如点击、滚动、输入等。事件监听的基本原理是,通过在特定元素上注册事件监听器,当事件在该元素上触发时,相应的处理函数会被执行。以下是事件监听的......
  • JavaScript WebApi 一(详讲)
    基础知识在前面的部分已经讲过了,大家如果没有学习过JavaScript的可以去看一下1.DOM引入在JavaScript中,DOM(文档对象模型)提供了一种表示和操作HTML文档的方式。在DOM中,文档被表示为一个由节点组成的树形结构。DOM对象则是这些节点的抽象表示,它们可以通过JavaScript来访问和操作。DOM......
  • Java医院医学AI智能导诊系统源码
    医院智能导诊系统是一款基于人工智能和大数据技术开发的医疗辅助软件,旨在为患者提供更加便捷、精准的医疗服务。一、什么是智能导诊系统?智能导诊系统是一种基于人工智能和大数据技术开发的医疗辅助软件,它能够通过对患者的症状、病史等信息进行计算分析,快速推荐科室和医生。通过简......
  • javascript基础学习系列一:标识符
    一般来说,语言中的标识符可以分为两类,一类用于命名语法(以及类型),一类用于命名值(的存储位置)。前者被称为“语法关键字”,后者则被称为“变量”和“常量”。由此引入了一个概念:绑定。从标识符的角度来说,绑定分为语法关键字与语法(语义)逻辑的绑定,以及变量与它所存储值的位置的绑定......
  • Python 爬虫,Nendo 网站作品信息采集爬虫源码!
    简单的网站写爬虫就跟流水线加工一样,抄抄改改,没有问题就直接上了,直接了当省事,又是一篇没有营养的水文。一个比较简单的爬虫,适合练手学习使用,主要是爬取和采集网站的作品信息,包括标题、内容及图片,其中图片采用了多线程爬取。考虑到外网爬取,所以采用了三次访问超时重试的机制,同时对于......