当前位置 博文首页 > Mzs-Qsy:深入浅出Promise

    Mzs-Qsy:深入浅出Promise

    作者:Mzs-Qsy 时间:2021-06-08 18:24


    Abstract

    Promise的意思是承诺(在红宝书中翻译为期约),新华字典:(动)对某项事务答应照办。

    Promise最早出现在Commn JS,随后形成了https://www.cnblogs.com/Code-Is-Fun/p/Promise/A规范。

    Promise是异步编程的一种解决方案,简单讲是一个容器其中保存这某个未来才会结束的事件的结果,从语法上说,Promise对象可以获取异步操作的消息,而且Promise提供了统一的API对于各种异步操作都可以用同样的方法进行处理

    比如我们发出请求调用服务器数据,由于网络延时原因,我们此时无法调用到数据,我们可以接着执行其它任务,等到将来某个时间节点服务器响应数据到达客户端,我们即可使用promise自带的一个回调函数来处理数据,所以要想真正理解Promise我们必须从回调开始

    1、回调函数定义

    回调是一个在另一个函数完成执行后所执行的函数——故此得名“回调”。

    2、为什么需要回调

    JavaScript 是一种事件驱动的单线程语言。这意味着,在继续之前, JavaScript 不会等待响应,而是继续执行且同时监听事件。

    举例:

    function first(){
      console.log(1);
    }
    function second(){
      console.log(2);
    }
    first();
    second();
    //1
    //2
    

    如果再first中有计时器呢?

    function first(){
      // 模拟代码延迟
      setTimeout( function(){
        console.log(1);
      }, 500 );
    }
    function second(){
      console.log(2);
    }
    first();
    second();
    //2
    //1
    //并不是 JavaScript 没有按照我们想要的顺序执行我们的函数,而是在继续执行 second() 之前, JavaScript 没有等待 first() 的响应。
    
    

    1.回调函数字面意思:就是回调就是一个函数的调用过程。那么就从理解这个调用过程开始吧。函数a有一个参数,这个参数是个函数b,当函数a执行完以后执行函数b。那么这个过程就叫回调。

    2.回调函数的意义:
    异步:一般ajax请求都是异步的。请求发出去后,处理器会继续执行下面的代码。如果你想ajax请求完成后,做一些事情,显然,直接在下一行写代码是达不到目的。而作为回调函数传给ajax请求,可以控制请求在哪个阶段去调用回调函数,并且不用担心后面的代码执行到什么地方了。

    3.回调函数的执行:就是异步的函数体执行成功或失败调用的传递进来的函数,调用的函数就是回调,为了不影响代码执行的效率,我们不会等待异步的代码执行,而是直接像下面执行,但是像请求,计时器,事件等操作我们在一些情况下必须拿到对应的数据或者返回值,这个时候就可以在异步函数里传入一个回调函数,也就是在异步操作执行结束之后会调用这个回调函数执行一段代码

    3.回调的局限性

    //普通函数
    // 第一步,打开冰箱
    function open(){
        setTimeout(()=>{
            console.log('打开冰箱');
            return 'success';
        }, 1000)
    }
    
    // 第二步,放牛进去
    function settle(){
          setTimeout(()=>{
           console.log('放牛进去');
           return 'success';
        }, 3000)
    }
    
    // 第三步,关上冰箱
    function close(){
          setTimeout(()=>{
           console.log('关上冰箱');
           return 'success';
        }, 1000)
    }
    
    function closeCow(){
        open();
        settle();
        close()
    }
    
    closeCow();
    
    //"打开冰箱"
    //"关上冰箱"?
    //"放牛进去"?
    
    
    
    
    
    
    
    //回调函数实现
    function closeCow() {
        setTimeout(() => {
            console.log("打开冰箱");
            setTimeout(() => {
                console.log("放牛进去");
                setTimeout(() => {
                    console.log("关闭冰箱");
                }, 1000);
            }, 3000);
        }, 1000);
    }
    

    如何解决回调嵌套?

    1.保持你的代码简短(给函数取有意义的名字,见名知意,而非匿名函数,写成一大坨)

    2.模块化(函数封装,打包,每个功能独立,可以单独的定义一个js文件Vue,react中通过import导入就是一种体现)

    3.Promise/生成器/ES6等

    4.Promise的特点

    特点1

    Promise有三种状态,分别是pending(进行中)、fulfilled(已成功)、rejected(已失败)。只有异步操作的结果可以决定当前是哪一种状态,任何其他操作都无法改变这个操作,Promise(承诺)这个名字也是由此而来,表示其他手段无法改变状态

    特点2

    如果状态发生了改变,就不会再改变而且任何时候都可以得到这个结果,Promise状态的改变只有两种情况一种是变为fulfilled另一种是变为rejected,改变后状态就凝固了不会再有任何改变,会一直保持这个结果,这是就成为resolved(已定形)。而且,如果改变已经发生你也可以对Promise添加回调函数获得结果,这与事件有根本的区别,事件如果不监听(dom.addEventListener),错过之后就无法再得到结果

    特点3

    无法取消Promise一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)

    5.Promise的使用

    基础语法

    const promise = new Promise((resolve, reject) => {
        resolve('fulfilled...'); // 状态由 pending --> fulfilled
    });
    
    promise.then(res => {
        console.log(res); // 只会调用 resolve
    }, err => {
        console.log(err); // 不会调用 rejected
    })
    // fulfilled
    

    Promise

    1.特性:

    ? 立即执行,自身是异步

    let promise = new Promise(function(resolve, reject) {
      console.log('Promise');
      resolve();
    });
    
    promise.then(function() {
      console.log('resolved.');
    });
    
    console.log('Hi!');
    // ?
    // ?
    // ?
    上面代码中,Promise 新建后立即执行,所以首先输出的是Promise。然后,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以resolved最后输出。
    

    2.特性

    ? 值穿透

    Promise.resolve(1)
      .then(2)
      .then(Promise.resolve(3))
      .then(console.log)
    
    // 输出 ?
    //Promise的then方法的参数期望是函数,传入非函数则会发生值穿透。
    

    6.Promise API

    比较简单,

    建议查看MDN文档

    7.手写Promise

    核心点

    1.状态的改变

    2.值的改变

    3.返回值

    4.class的使用

    基础版

    // 构建
    const PENDING = 'pending'//进行中
    const FULFILLED = 'fulfilled'//已成功
    const REJECTED = 'rejected'//已失败
    
    class NewPromise {
        //接受一个函数handle
      constructor (handle) {
      if (!isFunction(handle)) {
        throw new Error('MyPromise must accept a function as a parameter')
      }
      // 添加状态
      this._status = PENDING
      // 添加状态
      this._value = undefined
      // 添加成功回调函数队列
      this._fulfilledQueues = []
      // 添加失败回调函数队列
      this._rejectedQueues = []
      // 执行handle
      try {
          //执行回调函数,class是严格模式指向undefined,通过bind修改指向resove的指向
        handle(this._resolve.bind(this), this._reject.bind(this)) 
          //收集错误
      } catch (err) {
        this._reject(err)
      }
     }
    }
    
    
    
    
    //then方法的对应状态的回调执行时机,值的改变
    then (onFulfilled, onRejected) {
      const { _value, _status } = this
      switch (_status) {
        // 当状态为pending时,将then方法回调函数加入执行队列等待执行
        case PENDING:
          this._fulfilledQueues.push(onFulfilled)
          this._rejectedQueues.push(onRejected)
          break
        // 当状态已经改变时,立即执行对应的回调函数
        case FULFILLED:
          onFulfilled(_value)
          break
        case REJECTED:
          onRejected(_value)
          break
      }
      // 返回一个新的Promise对象
      return new MyPromise((onFulfilledNext, onRejectedNext) => {
      })
    }
    
    
    
    
    
    
    
    
    
    //那返回的新的 Promise 对象什么时候改变状态?改变为哪种状态呢?
    //then方法确认Promise状态
    then (onFulfilled, onRejected) {
      const { _value, _status } = this
      // 返回一个新的Promise对象
      return new MyPromise((onFulfilledNext, onRejectedNext) => {
        
      
          
          
          // 封装一个成功时执行的函数
        let fulfilled = value => {
          try {
            if (!isFunction(onFulfilled)) {
              onFulfilledNext(value)
            } else {
              let res =  onFulfilled(value);
              if (res instanceof MyPromise) {
                // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                res.then(onFulfilledNext, onRejectedNext)
              } else {
                //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                onFulfilledNext(res)
              }
            }
          } catch (err) {
            // 如果函数执行出错,新的Promise对象的状态为失败
            onRejectedNext(err)
          }
        }
        
        
        // 封装一个失败时执行的函数
        let rejected = error => {
          try {
            if (!isFunction(onRejected)) {
              onRejectedNext(error)
            } else {
                let res = onRejected(error);
                if (res instanceof MyPromise) {
                  // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                  res.then(onFulfilledNext, onRejectedNext)
                } else {
                  //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                  onFulfilledNext(res)
                }
            }
          } catch (err) {
            // 如果函数执行出错,新的Promise对象的状态为失败
            onRejectedNext(err)
          }
        }
        
        
       // 执行对应函数
        switch (_status) {
          // 当状态为pending时,将then方法回调函数加入执行队列等待执行
          case PENDING:
            this._fulfilledQueues.push(fulfilled)
            this._rejectedQueues.push(rejected)
            break
          // 当状态已经改变时,立即执行对应的回调函数
          case FULFILLED:
            fulfilled(_value)
            break
          case REJECTED:
            rejected(_value)
            break
        }
      })
    }
    
    

    完全版--1

    ES6语法实现Promise
      // 判断变量否为function
      const isFunction = variable => typeof variable === 'function'
      // 定义Promise的三种状态常量
      const PENDING = 'PENDING'
      const FULFILLED = 'FULFILLED'
      const REJECTED = 'REJECTED'
    
      class MyPromise {
        constructor (handle) {
          if (!isFunction(handle)) {
            throw new Error('MyPromise must accept a function as a parameter')
          }
          // 添加状态
          this._status = PENDING
          // 添加状态
          this._value = undefined
          // 添加成功回调函数队列
          this._fulfilledQueues = []
          // 添加失败回调函数队列
          this._rejectedQueues = []
          // 执行handle
          try {
              //class是严格模式,如果不bind会Uncaught TypeError: Cannot read property  undefined
            handle(this._resolve.bind(this), this._reject.bind(this)) 
          } catch (err) {
            this._reject(err)
          }
        }
        // 添加resovle时执行的函数
        _resolve (val) {
          const run = () => {
              //不是进行状态,已凝固直接返回
            if (this._status !== PENDING) return
            // 依次执行成功队列中的函数,并清空队列
            const runFulfilled = (value) => {
              let cb;
              while (cb = this._fulfilledQueues.shift()) {
                cb(value)
              }
            }
            // 依次执行失败队列中的函数,并清空队列
            const runRejected = (error) => {
              let cb;
              while (cb = this._rejectedQueues.shift()) {
                cb(error)
              }
            }
            /* 如果resolve的参数为Promise对象,则必须等待该Promise对象状态改变后,
              当前Promsie的状态才会改变,且状态取决于参数Promsie对象的状态
            */
            if (val instanceof MyPromise) {
              val.then(value => {
                this._value = value
                this._status = FULFILLED
                runFulfilled(value)
              }, err => {
                this._value = err
                this._status = REJECTED
                runRejected(err)
              })
            } else {
              this._value = val
              this._status = FULFILLED
              runFulfilled(val)
            }
          }
          // 为了支持同步的Promise,这里采用异步调用
          setTimeout(run, 0)
        }
        // 添加reject时执行的函数
        _reject (err) { 
          if (this._status !== PENDING) return
          // 依次执行失败队列中的函数,并清空队列
          const run = () => {
            this._status = REJECTED
            this._value = err
            let cb;
              //将删除的第一个元素的值赋值给cb
            while (cb = this._rejectedQueues.shift()) {
              cb(err)
            }
          }
          // 为了支持同步的Promise,这里采用异步调用
          setTimeout(run, 0)
        }
        // 添加then方法
        then (onFulfilled, onRejected) {
          const { _value, _status } = this
          // 返回一个新的Promise对象
          return new MyPromise((onFulfilledNext, onRejectedNext) => {
            // 封装一个成功时执行的函数
            let fulfilled = value => {
              try {
                if (!isFunction(onFulfilled)) {
                  onFulfilledNext(value)
                } else {
                  let res =  onFulfilled(value);
                  if (res instanceof MyPromise) {
                    // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                    res.then(onFulfilledNext, onRejectedNext)
                  } else {
                    //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                    onFulfilledNext(res)
                  }
                }
              } catch (err) {
                // 如果函数执行出错,新的Promise对象的状态为失败
                onRejectedNext(err)
              }
            }
            // 封装一个失败时执行的函数
            let rejected = error => {
              try {
                if (!isFunction(onRejected)) {
                  onRejectedNext(error)
                } else {
                    let res = onRejected(error);
                    if (res instanceof MyPromise) {
                      // 如果当前回调函数返回MyPromise对象,必须等待其状态改变后在执行下一个回调
                      res.then(onFulfilledNext, onRejectedNext)
                    } else {
                      //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                      onFulfilledNext(res)
                    }
                }
              } catch (err) {
                // 如果函数执行出错,新的Promise对象的状态为失败
                onRejectedNext(err)
              }
            }
            switch (_status) {
              // 当状态为pending时,将then方法回调函数加入执行队列等待执行
              case PENDING:
                this._fulfilledQueues.push(fulfilled)
                this._rejectedQueues.push(rejected)
                break
              // 当状态已经改变时,立即执行对应的回调函数
              case FULFILLED:
                fulfilled(_value)
                break
              case REJECTED:
                rejected(_value)
                break
            }
          })
        }
        // 添加catch方法
        catch (onRejected) {
          return this.then(undefined, onRejected)
        }
        // 添加静态resolve方法
        static resolve (value) {
          // 如果参数是MyPromise实例,直接返回这个实例
          if (value instanceof MyPromise) return value
          return new MyPromise(resolve => resolve(value))
        }
        // 添加静态reject方法
        static reject (value) {
          return new MyPromise((resolve ,reject) => reject(value))
        }
        // 添加静态all方法
        static all (list) {
          return new MyPromise((resolve, reject) => {
            /**
             * 返回值的集合
             */
            let values = []
            let count = 0
            for (let [i, p] of list.entries()) {
              // 数组参数如果不是MyPromise实例,先调用MyPromise.resolve
              this.resolve(p).then(res => {
                values[i] = res
                count++
                // 所有状态都变成fulfilled时返回的MyPromise状态就变成fulfilled
                if (count === list.length) resolve(values)
              }, err => {
                // 有一个被rejected时返回的MyPromise状态就变成rejected
                reject(err)
              })
            }
          })
        }
        // 添加静态race方法
        static race (list) {
          return new MyPromise((resolve, reject) => {
            for (let p of list) {
              // 只要有一个实例率先改变状态,新的MyPromise的状态就跟着改变
              this.resolve(p).then(res => {
                resolve(res)
              }, err => {
                reject(err)
              })
            }
          })
        }
        finally (cb) {
          return this.then(
            value  => MyPromise.resolve(cb()).then(() => value),
            reason => MyPromise.resolve(cb()).then(() => { throw reason })
          );
        }
      }
    

    注释:

    1.Try

    2.consturctor

    完全版-2

    ES5语法实现
    (function () {
        // 判断function
        function isFunction(fn) {
            return typeof fn === 'function';
        }
    
        // 状态 pending、fulfilled、rejected
        var PENDING = 'pending';
        var FULFILLED = 'fulfilled';
        var REJECTED = 'rejected';
    
        // 构造方法
        var Kromise = function (handle) {
            // 当前状态
            this._status = PENDING;
            // 添加成功回调队列
            this._fulfilledQueue = [];
            // 添加失败回调队列
            this._rejectedQueue = [];
            // 引用当前this对象
            var self = this;
    
            if (!isFunction(handle)) {
                throw new Error('Parameter handle is not a function!')
            }
    
            // 添加resolve时执行的函数
            function _resolve(val) {
                var run = function () {
                    if (self._status !== PENDING) return;
                    // 依次执行成功队列中的函数,并清空队列
                    var runFulfilled = function (res) {
                        var resolve;
                        while (resolve = self._fulfilledQueue.shift()) { // 出栈
                            resolve(res);
                        }
                    };
    
                    // 依次执行失败队列中的函数,并清空队列
                    var runRejected = function (err) {
                        var reject;
                        while (reject = self._rejectedQueue.shift()) { // 出栈
                            reject(err);
                        }
                    };
                    /* 如果resolve的参数为Kromise对象,则必须等待该Kromise对象状态改变后,
                     * 当前Kromise的状态才会改变,且状态取决于参数Kromise对象的状态
                     */
                    if (val instanceof Kromise) {
                        val.then(function (value) {
                            self._status = FULFILLED;
                            self._value = value;
                            runFulfilled(value)
                        }, function (err) {
                            self._status = REJECTED;
                            self._value = err;
                            runRejected(err);
                        })
                    } else {
                        self._status = FULFILLED;
                        self._value = val;
                        runFulfilled(val);
                    }
    
                };
                // 为了支持同步的Promise,这里采用异步调用
                setTimeout(run, 0)
            }
    
            // 添加reject时执行的函数
            function _reject(err) {
                var run = function () {
                    if (self._status !== PENDING) return;
                    // 依次执行成功队列中的函数,并清空队列
                    self._status = REJECTED;
                    self._value = err;
                    var reject;
                    while (reject = self._fulfilledQueue.shift()) { // 出栈
                        reject(err);
                    }
                };
                // 为了支持同步的Promise,这里采用异步调用
                setTimeout(run, 0)
            }
    
            // 执行handle,捕获异常
            try {
                handle(_resolve.bind(this), _reject.bind(this));
            } catch (e) {
                _reject(e);
            }
        };
    
        // 属性
        Kromise.length = 1;
    
        // 实例方法
        // 实现then方法
        Kromise.prototype.then = function (onFulfilled, onRejected) {
            var self = this;
            // 返回一个新的Kromise对象
            return new Kromise(function (onFulfilledNext, onRejectedNext) {
                // 成功时的回调
                var fulfilled = function (val) {
                    try {
                        // 如果不是函数,值穿透
                        if (!isFunction(onFulfilled)) {
                            onFulfilledNext(val)
                        } else {
                            var res = onFulfilled(val);
                            // 如果当前回调函数返回Kromise对象,必须等待其状态改变后在执行下一个回调
                            if (res instanceof Kromise) {
                                res.then(onFulfilledNext, onRejectedNext);
                            } else {
                                //否则会将返回结果直接作为参数,传入下一个then的回调函数,并立即执行下一个then的回调函数
                                onFulfilledNext(res);
                            }
                        }
                    } catch (e) {
                        // 如果函数执行出错,新的Kromise对象的状态为失败
                        onRejectedNext(e);
                    }
                };
                // 失败时的回调
                var rejected = function (err) {
                    try {
                        if (!isFunction(onRejected)) {
                            onRejectedNext(err)
                        } else {
                            var res = onRejected(err);
                            if (res instanceof Kromise) {
                                res.then(onFulfilledNext, onRejectedNext);
                            } else {
                                onFulfilledNext(res);
                            }
                        }
                    } catch (e) {
                        onRejectedNext(e)
                    }
                };
    
                switch (self._status) {
                    // 当状态为pending时,将then方法回调函数加入执行队列等待执行
                    case PENDING:
                        self._fulfilledQueue.push(fulfilled);
                        self._rejectedQueue.push(rejected);
                        break;
                    // 当状态已经改变时,立即执行对应的回调函数
                    case FULFILLED:
                        fulfilled(self._value);
                        break;
                    case REJECTED:
                        rejected(self._value);
                        break;
                }
            });
        };
    
        // 实现catch方法
        Kromise.prototype.catch = function (onRejected) {
            return this.then(undefined, onRejected);
        };
    
        // 实现finally方法
        Kromise.prototype.finally = function (onFinally) {
            return this.then(function (value) {
                Kromise.resolve(onFinally()).then(function () {
                    return value;
                })
            }, function (err) {
                Kromise.resolve(onFinally()).then(function () {
                    throw new Error(err);
                })
            })
        };
    
        // 静态方法
        // 实现resolve方法
        Kromise.resolve = function (value) {
            // 如果参数是Kromise实例,直接返回这个实例
            if (value instanceof Kromise) {
                return value;
            }
            return new Kromise(function (resolve) {
                resolve(value)
            })
        };
        // 实现reject方法
        Kromise.reject = function (value) {
            return new Kromise(function (resolve, reject) {
                reject(value)
            })
        };
        // 实现all方法
        Kromise.all = function (arr) {
            var self = this;
            return new Kromise(function (resolve, reject) {
                var values = [];
                for (var i = 0, len = arr.length; i < len; i++) {
                    // 数组参数如果不是Kromise实例,先调用Kromise.resolve
                    self.resolve(arr[i]).then(function (res) {
                        values.push(res);
                        // 所有状态都变成fulfilled时返回的Kromise状态就变成fulfilled
                        if (values.length === arr.length) {
                            resolve(values);
                        }
                    }, function (e) {
                        // 有一个被rejected时返回的Kromise状态就变成rejected
                        reject(e);
                    })
                }
            })
        };
    
        // 实现race方法
        Kromise.race = function (arr) {
            var self = this;
            return new Kromise(function (resolve, reject) {
                for (var i = 0, len = arr.length; i < len; i++) {
                    // 只要有一个实例率先改变状态,新的Kromise的状态就跟着改变
                    self.resolve(arr[i]).then(function (res) {
                        resolve(res);
                    }, function (err) {
                        reject(err);
                    })
                }
            })
        };
        // 实现any方法
        Kromise.any = function (arr) {
            var self = this;
            return new Kromise(function (resolve, reject) {
                var count = 0;
                var errors = [];
                for (var i = 0, len = arr.length; i < len; i++) {
                    // 只要有一个实例状态变为fulfilled,新的Kromise状态就会改变为fulfilled
                    self.resolve(arr[i]).then(function (res) {
                        resolve(res);
                    }, function (err) {
                        errors[count] = err;
                        count++;
                        // 否则等待所有的rejected,新的Kromise状态才会改变为rejected
                        if (count === arr.length) {
                            reject(errors);
                        }
                    })
                }
            })
    
        };
        // 实现allSettled方法
        Kromise.allSettled = function (arr) {
            var results = [];
            var len = arr.length;
            for (var i = 0; i < len; i++) {
                this.resolve(arr[i]).then(function (res) {
                    results.push({status: FULFILLED, value: res});
                }, function (err) {
                    results.push({status: REJECTED, value: err});
                })
            }
            // 一旦结束,状态总是`fulfilled`,不会变成`rejected`
            return new Kromise(function (resolve, reject) {
                resolve(results)
            })
        };
        // 实现try方法
        Kromise.try = function (fn) {
            if (!isFunction(fn)) return;
            return new Kromise(function (resolve, reject) {
                return resolve(fn());
            })
        };
    
        // 挂载
        window.Kromise = Kromise;
    })();
    
    

    总结

    promise通过自己的回调嵌套解决别人的问题

    8.参考文档

    [1].JavaScript | MDN

    [2].Promise迷你书

    [3].廖雪峰的官方网站

    [4].ES6网道教程

    bk