• Promise期约函数的实现


    前言

    Promise也叫期约函数,是ES6中新增的特性,是解决异步编程的一种方案,取代回调函数避免回调地狱

    const p = new Promise((resolve,reject)=>{
      resolve(1);
    });
    
    // 链式调用
    p.then(res => Promise.resolve(res + 2))
     .then(res => Promise.resolve(res + 3))
     .then(res => console.log(res));
     // 6
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    Promise的一些特性和约定:

    • Promise是一个构造函数,接受一个executor函数,executor在new的时候会立即执行,executor接收resolve和reject两个函数作为参数。
    • 期约函数有三个状态,pending、fullfiled、reject,初始为pending状态,只能通过resolve和reject改变期约的状态,并且这个过程不可逆。

    Promse的实现思路
    Promise的核心就是通过resolve/reject函数获取到value/reason,通过then函数获得回调函数,然后将value传入回调函数执行,通过then函数保证执行顺序。具体过程如下:

    1. 通过resolve/reject获取到value/reason
    2. 通过then函数获取callback
    3. 通过then函数保证callback的执行顺序

    Promise本质上就是通过then函数将callback串联执行,这点和compose有点类似。

    1. Promise构造函数

    • 只能通过resolve/reject改变期约函数的状态,状态改变不可逆
    • executor函数立即执行
        function MyPromise(executor) {
          this.value = undefined;
          this.reason = undefined;
          this.state = 'pending';
          this.onResolveCllbacks = [];
          this.onRejectCllbacks = [];
    
          const resolve = (val) => {
            if (this.state === 'pending') {
              this.value = val;
              this.state = 'fullfilled';
              // 执行保存的回调
              if (this.onResolveCllbacks.length > 0) {
                this.onResolveCllbacks.forEach(fn => fn(this.value));
                this.onResolveCllbacks = [];
              };
            }
          }
    
          const reject = (reason) => {
            if (this.state === 'pending') {
              this.reason = reason;
              this.state = 'reject';
              // 执行保存的回调
              if (this.onRejectCllbacks.length > 0) {
                this.onRejectCllbacks.forEach(fn => fn(this.reason));
                this.onRejectCllbacks = [];
              };
            }
          }
          executor(resolve, reject);
        };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    2. Array.prototy.then函数实现

    then函数接收onResolve和onReject两个参数作为回调函数,并返回一个新的Promise,返回Promise的规则如下:

    • 没有传递onResolve/onReject或者它们没有返回值的时候,直接返回原来的Pomise函数
    • onResolve/onReject的返回值是个Promsie的时候直接返回这个Promise
    • onResolve/onReject返回值为其它类型的时候,相当于返回Promise.resolve(onResolve())或Promise.reject(onReject())
        MyPromise.prototype.then = function (onResolve, onReject) {
          if (this.state === 'fullfilled') {
            if (typeof onResolve === 'function') {
              const result = onResolve?.(this.value);
              return result instanceof MyPromise ? result : result ? new MyPromise((resolve, reject) => resolve(result)) : this;
            } else {
              return this;
            };
          };
          if (this.state === 'reject') {
            if (typeof onReject === 'function') {
              const result = onReject?.(this.reason);
              return result instanceof MyPromise ? result : result ? new MyPromise((resolve, reject) => reject(result)) : this;
            } else {
              return this;
            }
          };
    
          // 如果此时还没有通过resolve得到value就存到数组中,等到resolve的时候执行
          if (this.state === 'pending') {
            return new MyPromise((resolve, reject) => {
              typeof onResolve === 'function' && this.onResolveCllbacks.push((value) => {
                const result = onResolve(value);
                if (result instanceof MyPromise) {
                  result.then(res => resolve(res), reason => reject(reason));
                } else {
                  resolve(result);
                };
              });
              typeof onReject === 'function' && this.onRejectCllbacks.push((reason) => {
                const result = onReject(reason);
                if (result instanceof MyPromise) {
                  result.then(res => resolve(res), reason => reject(reason));
                } else {
                  resolve(result);
                };
              });
            });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    then函数的逻辑要考虑两种情况,一种是resolve函数先于then函数执行,这种情况发生在excutor的resolve语句为同步代码:

       const p  = new MyPromise((resolve,reject)=>resolve('result'));
    
    • 1

    当executor的resolve包含异步操作时,then函数是先于resolve执行的

        const p  = new MyPromise((resolve,reject)=>{
          setTimeout(()=>{
            resolve('result');
          },200)
        });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 针对第一种情况直接then函数中直接能获取到resolve/reject的value/reason,然后将其传入onResolve/onReject执行即可。对于第二种情况,要先将onResolve/onReject保存起来,等到resolve/reject获取到value/reason的时候再执行。

    2. 第二种情况下对于then函数返回的Promsie也要特殊处理,then函数返回Promise是依赖于父级Promise的state和value/reason的,具体的处理方法是直接返回一个新的Promise,把它的resolve和reject方法通过闭包保存下来,等到resolve的时候执行。

    onResolveCllbacks和onRejectCllbacks之所以是个数组是因为then函数可以被多次调用,会产生多个回调函数

       var p = new MyPromise((resolve, reject) => {
          setTimeout(() => {
            resolve(1);
          }, 2000);
        });
        p.then(res=>console.log('第一次调用',res));
        p.then(res=>console.log('第二次调用',res));
        p.then(res=>console.log('第三次调用',res));
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3. 基础使用

    3.1 链式调用

        const p = new MyPromise((resolve, reject) => {
          setTimeout(() => {
            resolve(1);
          }, 100);
        });
    
        function createPromise(initialValue, value) {
          return new MyPromise((resolve, reject) => {
            setTimeout(() => {
              resolve(initialValue + value)
            }, 200);
          })
        };
        
        p.then(res=>createPromise(res,2))
        .then(res=>createPromise(res,3))
        .then(res=>console.log(res)); 
        // 6
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.2 执行同步和异步代码

        // 同步操作
        var p1 = new MyPromise((resolve, reject) => {
            resolve(1);
        });
        p1.then(res=>console.log(res)); //1
        
        // 异步操作
        var p2 = new MyPromise((resolve, reject) => {
           setTimeout(()=> resolve(1);,100)
        });
        p2.then(res=>console.log(res)); //1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    3.3 多次调用

        var p = new MyPromise((resolve, reject) => {
          setTimeout(() => {
            resolve(1);
          }, 100);
        });
    
        p.then(res=>console.log('第一次调用',res)); //第一次调用 1
        p.then(res=>console.log('第二次调用',res)); //第二次调用 1
        p.then(res=>console.log('第三次调用',res)); //第三次调用 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4. Array.prototy.catch函数实现

    catch函数是onReject的语法糖,onReject与catch同时存在时只会执行onReject。catch函数也返回一个Promise,只不过这个场景很少用,下面的代码中不包含返回Promsie的部分。

        MyPromise.prototype.catch = function (onReject) {
          if (this.state === 'reject') {
            onReject?.(this.reason)
          } else {
            typeof onReject === 'function' && this.onRejectCllbacks.push(onReject);
          };
        };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5. Promis.resolve、Promsie.reject的实现

        MyPromise.resolve = function (value) {
          return new MyPromise((resolve, reject) => resolve(value))
        };
    
        MyPromise.reject = function (reason) {
          return new MyPromise((resolve, reject) => reject(reason));
        };
    
        //使用
        const p1 = MyPromise.resolve('result');
        p1.then(res => console.log(res)); //result
    
        const p2 = MyPromise.reject('reason');
        p2.then(null, reason => console.log(reason)); //reason
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    6. Promise.all的实现

    Promise.all用来执行多个Promise,返回一个新的Promise,返回的这个promise规则如下:

    • 所有的promise都成功,Promsie.all才算成功,将它们的结果放到数组中作为新的promise.all的结果
    • 有一个promsie失败,Promsie.all就算失败,将第一个出现错误的Promise的信息作为reject的信息

    Promise起到的作用是按顺序执行Promise,将它们的结果序列化。

        MyPromise.all = function (fns) {
            const res = [];
            let count = 0;
            return new MyPromise((resolve, reject) => {
              function next() {
                fns[count].then(res => {
                  if (count === fns.length) {
                    resolve(res);
                  } else {
                    count++
                    res.push(res);
                  };
                }, reason => {
                    reject(reason);
                });
              };
              next();
            });
          }; 
          
     //使用
     var a = new MyPromise((resolve,reject)=>setTimeout(() => {
        resolve(1)
      }, 200));
      var b = new MyPromise((resolve,reject)=>setTimeout(() => {
        resolve(2);
      }, 100));
    
      var c = new MyPromise((resolve,reject)=>setTimeout(() => {
        resolve(3)
      }, 300));
    
      var p = Promise.all([a,b,c]);
      p.then(res=>console.log(res)); //[1,2,3]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    7. Promise.race的实现

    race函数接收一个promsie数组进行赛马,返回一个新的promsie,并且这个promsie的结果依赖于先跑完的promise。

        MyPromise.race = function (promiseFns) {
          return new MyPromise((resolve, reject) => {
            promiseFns.forEach((fn) => {
              fn.then(res => resolve(res), reason => reject(reason));
            });
          })
        };
        
      // 使用
      var a = new MyPromise((resolve,reject)=>setTimeout(() => {
        resolve(1)
      }, 500));
      var b = new MyPromise((resolve,reject)=>setTimeout(() => {
        resolve(2);
      }, 800));
    
      var c = new MyPromise((resolve,reject)=>setTimeout(() => {
        resolve(3)
      }, 300));
    
      var p = Promise.race([a,b,c]);
      p.then(res=>console.log(res),reason=>console.log(reason)); //3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    8. promsie的串行

    使用compose串行promsie

        function add1(x) {
          return x + 1;
        };
        function add3(x) {
          return x + 3;
        };
        function add5(x) {
          return x + 5;
        };
    
        const fns = [add1,add3,add5];
        const add9 = fns.reduce((promise,cb)=>promise.then(cb),Promise.resolve(1));
        add9.then(res=>console.log(res)); //10
    
        //进一步封装
        const add = fns => x => fns.reduce((promsie, cb) => promsie.then(cb), Promise.resolve(x));
        const add9 = add([add1, add3, add5]);
        add9(10).then(res => console.log(res)); //19
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    9. Promise的不足

    • pending状态无法判断是刚开始还是已经结束,状态的变化没有通知
    • 一旦创建无法取消
      var a = new Promise(()=>{}).then();
      console.log(a);
    
      var b = Promise.resolve().then(res=>new Promise(()=>{}));
      console.log(b);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这里插入图片描述
    promsie无法取消的问题可以通过添加方法手动实现,或者使用一些像cancelPromise这样的三方库,下面的代码利用Promsie.race简单实现了对promsie的中断

        function cancelPromsie(promise) {
            Promise.race([promise, new Promise((resolve, reject) => { resolve('cancel') })]);
          };
    
          var p1 = new Promise((resolve, reject) => {
            setTimeout(() => {
              console.log('p1')
              resolve('p1');
            }, 1000);
          });
    
          cancelPromsie(p1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    Promise的取消本意上是停止掉不需要的异步函数,取消promsie本身没有太大的意义,es6之所以没有支持取消promsie是因为取消promise会使得then函数的链式调用和Promsie.all的执行逻辑更加复杂。

  • 相关阅读:
    开源软件 FFmpeg 生成模型使用图片数据集
    RabbitMQ (四)高级队列
    Android系统的Ashmem匿名共享内存子系统分析(2)- 运行时库cutils的Ashmem访问接口
    南卡和益博思哪个电容笔比较好?国产电容笔全方位对比
    2022.10.9-10.16 AI行业周刊(第119期):相信坚持的力量
    外卖霸王餐项目来啦 微客云免费提供霸王餐系统
    xxl-job架构原理讲解
    文件共享服务器(cifs协议 -- 微软开发)
    react多条件查询
    uniapp IOS上架AppStore因打开相机、相册提示不明确被拒
  • 原文地址:https://blog.csdn.net/qq_44621394/article/details/128088789