zoukankan      html  css  js  c++  java
  • Promise是如何实现异步编程的?

    Promise标准

    不能免俗地贴个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 : (v) => v;
        onRejected = self._isFunction(onRejected) ? onRejected : (v) => v;
    
        return new MyPromise((resolve, reject) => {
          const fulfilled = (value) => {
            const res = onFulfilled(value);
            if (res instanceof MyPromise) {
              res.then(resolve, reject);
            } else {
              resolve(res);
            }
          };
          const rejected = (value) => {
            const res = onRejected(value);
            if (res instanceof MyPromise) {
              // 这里是重点
              res.then(resolve, reject);
            } else {
              // 注意这里是resolve(res),而不是reject(res)
              resolve(res);
            }
          };
          switch (_status) {
            case "pending":
              self._handleFulfilled.push(fulfilled);
              self._handleRejected.push(rejected);
              break;
            case "fulfilled":
              fulfilled(_value);
              break;
            case "rejected":
              rejected(_value);
              break;
            default:
              throw new Error('Promise resolver Unverified status');
          }
        });
      }
    

    完整代码

    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);
        }
      }
      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) => {
          const fulfilled = (value) => {
            const res = onFulfilled(value);
            if (res instanceof MyPromise) {
              res.then(resolve, reject);
            } else {
              resolve(res);
            }
          };
          const rejected = (value) => {
            const res = onRejected(value);
            if (res instanceof MyPromise) {
              // 这里是重点
              res.then(resolve, reject);
            } else {
              // 注意这里是resolve(res),而不是reject(res)
              resolve(res);
            }
          };
          switch (_status) {
            case "pending":
              self._handleFulfilled.push(fulfilled);
              self._handleRejected.push(rejected);
              break;
            case "fulfilled":
              fulfilled(_value);
              break;
            case "rejected":
              rejected(_value);
              break;
            default:
              throw new Error('Promise resolver Unverified status');
          }
        });
      }
    }
    

    测试一下,先输出1,3秒后输出2,说明MyPromise的基本功能没问题了。

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

    最后,总结一下,Promise是如何实现异步编程的?

    Promise接收一个函数为参数,传入了两个内部的方法resolve和reject,然后用then注册回调函数,手动调用resolve或reject就可以依次执行then的回调,并且给回调函数传值。如果then返回的也是Promise,同样的,手动调用resolve或reject后,才会继续往下执行。

    其实本质上还是回调函数,只不过写法变了。

    本文GitHub链接:Promise是如何实现异步编程的?

  • 相关阅读:
    c#备忘知识点
    [置顶] IE6支持的滑动菜单栏
    摩斯电碼中英文对照表
    2013年5月15日星期三
    2013年5月9日星期四
    2013年5月12日16:20:43母亲节
    2013年5月8日星期三
    JS前端DOM中Range疑问
    2013年5月14日星期二
    2013年5月13日星期一
  • 原文地址:https://www.cnblogs.com/wangmeijian/p/14209230.html
Copyright © 2011-2022 走看看