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

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);
    };

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);
            };
          });
        });

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

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

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

    const p  = new MyPromise((resolve,reject)=>{
      setTimeout(()=>{
        resolve('result');
      },200)
    });
  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));

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

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

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

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);
      };
    };

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

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]

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

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

9. Promise的不足

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

  var b = Promise.resolve().then(res=>new Promise(()=>{}));
  console.log(b);

在这里插入图片描述
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);

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值