Promise是如何实现异步编程的?

亚瑟
• 阅读 636

不能免俗地贴个Promise标准链接Promises/A+。ES6的Promise有很多方法,包括Promise.all()/Promise.resolve()/Promise.reject()等,但其实这些都是Promises/A+规范之外的,Promises/A+规范只定义了一个Promise.then()方法,这是Promise的核心。

基本结构

new Promise((resolve, reject) => {
  let a = 0;
  if (a > 1) {
    resolve(a);
  } else {
    reject(a);
  }
}).then(res => {
  console.log(res);
}, err => {
  console.log(err);
}) 

Promise接收一个函数作为参数,我们称之为executor,该函数有两个参数resolve和reject,这两个参数也都是函数,并且,它们定义在Promise内部。

那么我们定义一个class并定义一个_isFunction方法,用来校验构造函数的参数必须是函数。再定义resolve和reject这两个方法。

class MyPromise{
  constructor(executor){
    if(!this._isFunction(executor)){
      throw new Error(`Promise resolver ${executor} is not a function`);
    }
  }
  _isFunction(val){
    return Object.prototype.toString.call(val) === '[object Function]';
  }
  _resolve(){
    
  }
  _reject(){
    
  }
} 

Promise状态、resolve、reject

Promise有三种状态,分别是pending(等待中)、fulfilled(成功)、rejected(失败)。状态改变只能从pending => fulfilled,或者pending => rejected。

resolve的作用,就是将Promise的状态从pending改为fulfilled,它接收一个参数作为Promise执行成功的值,这个值会传给then的第一个回调函数。reject的作用是将Promise的状态从pending改为rejected,它也接收一个参数作为Promise执行失败的值,这个值会传给then的第二个回调函数。

那么我们定义好状态_status、_resolve、_reject,再定义两个数组_handleFulfilled、_handleRejected,分别存放then的成功和失败回调集合。当用户调用resolve或reject方法后,开始异步调用_handleFulfilled或_handleRejected数组中的回调。

class MyPromise {
  constructor(executor) {
    if (!this._isFunction(executor)) {
      throw new Error(`${executor} is not a function`);
    }
    this._status = "pending";
    this._value = undefined;
    this._handleFulfilled = [];
    this._handleRejected = [];
    // 很多文章在这里给executor加了try catch,实际上原生Promise的executor中的错误并没有捕获
    executor(this._resolve.bind(this), this._reject.bind(this));
  }
  _isFunction(val) {
    return Object.prototype.toString.call(val) === "[object Function]";
  }
  _resolve(value) {
    if(this._status === 'pending'){
      this._status = "fulfilled";
      this._value = value;
      let cb;
      // 异步按顺序调用并清空回调
      setTimeout(() => {
        while(cb = this._handleFulfilled.shift()){
          cb(value);
        }
      }, 0)
    }
  }
  _reject(value) {
    if(this._status === 'pending'){
      this._status = "rejected";
      this._value = value;
      let cb;
      // 异步按顺序调用并清空回调
      setTimeout(() => {
        while ((cb = this._handleRejected.shift())) {
          cb(value);
        }
      }, 0);
    }
  }
} 

Promise.then

Promise.then定义了两个回调onFulfilled和onRejected

promise.then(onFulfilled, onRejected) 

它们分别在Promise执行成功/失败时执行,它们都是可选的,Promises/A+规范规定,如果onFulfilled或onRejected不是函数,将被忽略,Promise会继续执行下一个then的回调。比如下面的例子会输出1,.then(2)则被忽略了。

new Promise((resolve, reject) => {
  resolve(1);
})
  .then(2)
  .then((res) => {
    console.log(res);
  }); 

then可以链式调用,是因为每个then都会返回一个新的Promise。为什么要返回新的Promise?因为每个then都可以返回不同的值,如果用同一个Promise,状态变化后是不能再修改的,无法做到返回不同的值。

then的回调执行onFulfilled还是onRejected,取决于Promise的状态,如果Promise状态为pending,只会将onFulfilled和onRejected分别push到_handleFulfilled和_handleRejected数组;如果状态为fulfilled,会执行对应的onFulfilled;如果状态是rejected,执行对应的onRejected;

那么then方法的基本结构如下

then(onFulfilled, onRejected) {
    const self = this;
    const { _value, _status } = this;

  // 如果onFulfilled、onRejected不是函数,强制改为函数,并且该函数直接返回接收到的参数,传后面的then的回调函数
  onFulfilled = self._isFunction(onFulfilled) ? onFulfilled : (v) => v;
  onRejected = self._isFunction(onRejected) ? onRejected : (v) => v;

  return new MyPromise((resolve, reject) => {
    switch (_status) {
      case "pending":
        self._handleFulfilled.push(onFulfilled);
        self._handleRejected.push(onRejected);
        break;
      case "fulfilled":
        onFulfilled(_value);
        // todo
        break;
      case "rejected":
        onRejected(_value);
        // todo
        break;
      default:
        throw new Error('Promise resolver Unverified status');
        break;
    }
  });
} 

在then链式调用的情况下,如果前一个then返回的是一个新Promise,后一个then的回调必须等这个新Promise的状态改变后才会执行。举例,下面的代码输出1之后,等待3秒才会输出2:

new Promise(resolve => {
  resolve()
}).then(() => {
  return new Promise(resolve => {
    console.log(1);
    setTimeout(() => {
      resolve()
    }, 3000)
  })
}).then(() => {
  console.log(2);
}) 

因此要对then的回调函数的返回值做个判断,如果返回值不是Promise,利用resolve直接返回这个值;如果返回值是Promise,就要等这个Promise状态变化之后再返回,而Promise状态变化之后一定会调用then的回调函数,利用这个特性,将resolve、reject作为then的回调函数即可。

then(onFulfilled, onRejected) {
    const self = this;
    const { _value, _status } = this;

    // 如果onFulfilled、onRejected不是函数,强制改为函数,并且该函数直接返回接收到的参数,传后面的then的回调函数
    onFulfilled = self._isFunction(onFulfilled) ? onFulfilled : (
点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Chase620 Chase620
4年前
Promise从入门到拿Offer之手写Promise
1、Promise构造函数的实现Promise构造函数用来声明示例对象,需要传入一个执行器函数。其中包括resolve函数和reject函数,以及几个重要的属性:状态属性、结果属性和回调函数队列。构造函数的基本框架resolve函数用于异步处理成功后调用的函数。其中包括验证对象状态修改次数,修改promise实例对象状态,异步调用成功的回调函数
Stella981 Stella981
3年前
JavaScript中Promise 使用、原理以及实现过程
1.什么是Promisepromise是目前JS异步编程的主流解决方案,遵循Promises/A方案。2.Promise原理简析(1)promise本身相当于一个状态机,拥有三种状态pendingfulfilledrejected一个promise对象初始化时
Stella981 Stella981
3年前
ES6 Promise 让异步函数顺序执行
应用ES6的内置对象Promise,让异步函数按顺序执行的例子如下:!(https://oscimg.oschina.net/oscnet/9d7da385127c5df3cdf50b984cc4bca58c7.jpg)上边是四个用Promise处理过的异步执行的函数:fn1、fn2、fn3、fn4下面,让其按顺序执行
Stella981 Stella981
3年前
Promise的奇怪用法和自己实现一个Promise
原文链接: Promise的奇怪用法和自己实现一个Promise(https://my.oschina.net/ahaoboy/blog/4645165)使用Promise实现一个页面所有图片加载完毕的回调importReact,{useEffect}from"react";exportdefault()
Stella981 Stella981
3年前
Javascript 中的神器——Promise
_摘要:_ 回调函数真正的问题在于他剥夺了我们使用return和throw这些关键字的能力。而Promise很好地解决了这一切回调函数真正的问题在于他剥夺了我们使用return和throw这些关键字的能力。而Promise很好地解决了这一切Promise概念所谓Promise,就是ES6原生提供的一个
Stella981 Stella981
3年前
ES6中的Promise和Generator详解
简介ES6中除了上篇文章讲过的语法新特性和一些新的API之外,还有两个非常重要的新特性就是Promise和Generator,今天我们将会详细讲解一下这两个新特性。Promise什么是PromisePromise是异步编程的一种解决方案,比传统的解决方案“回调函数和事件”更合理和更强大。所谓P
Stella981 Stella981
3年前
ES6中自带的promise的问题
ES6中的Promise作用:解决异步嵌套问题。但是只能解决两层嵌套,不能解决两层以上嵌套;解决方法是我自己封装的函数如下:functionPromising(){var_arr;this.thenfunction(fx){
Promise规范与原理解析 | 京东物流技术团队
Promise对象用于清晰的处理异步任务的完成,返回最终的结果值,本次分享主要介绍Promise的基本属性以及Promise内部的基础实现,能够帮我们更明确使用场景、更快速定位问题。
京东云开发者 京东云开发者
7个月前
Promise规范与原理解析
作者:京东物流孙琦摘要Promise对象用于清晰的处理异步任务的完成,返回最终的结果值,本次分享主要介绍Promise的基本属性以及Promise内部的基础实现,能够帮我们更明确使用场景、更快速定位问题。Promise出现的原因首先我们先来看一段代码:异步
亚瑟
亚瑟
Lv1
此夜曲中闻折柳,何人不起故园情。
文章
3
粉丝
0
获赞
0