手动实现Promise、Promise.All、Promise.AllSettled


最近在整理promise的一些知识点,以前看的太浅,如今再去理解又是另外一种感觉,废话就不多说了,直接上原理

Promise原理

promise函数是ES6里的一部分,new promise对象,promise其实也是一个构造函数,自身有reject、resolve、race方法,原型链上有then、catch方法。用new出来一个promise对象会自带这些方法

promise有两个特点

1、 Promise对象的状态不受外界影响

  • pending(进行中)
  • fulfilled(已成功)
  • rejected(已失败)

2、一旦状态改变,就不会再变,任何时候都可以得到这个结果,状态改变,只有两种情况

  • 从pending变为fulfilled
  • 从pending变为rejected

手动实现Promise(Class vs ES5)

以前都不太喜欢使用class的写法来实现一个功能,直到有一天我翻遍了网上手动实现promise的ES5代码后,觉得难读难理解,代码还不整洁,官方给的实现方法也just so so,偶然我看到一篇使用class写法,真的超级香,很清爽也易读易懂易实现,代码还很优雅,果断记录下来

class MyPromise {
  static Pending = "pending";
  static Fulfiled = "fulfiled";
  static Reject = "reject";

  constructor(executor) {
    this.status = MyPromise.Pending; // Promise实例的状态,初始化为pending
    this.result = undefined; // result保存异步操作成功时的返回值
    this.error = undefined; // error保存异步操作失败时的返回值
    this.callbacks = []; // 保存then方法传入的回调函数,在实例状态改变时调用
    // 执行传入的函数,为入参resolve和reject绑定this
    console.log("执行传入的函数");
    executor(this._resolve.bind(this), this._reject.bind(this));
  }

  // 实现resolve函数
  _resolve(result) {
    if (result instanceof MyPromise) {
      // 表示onSuccess返回结果是一个Promise实例,假定这个实例为a
      // 我们是想获取这个叫a的Promise实例里面的result,但是a实例并没有暴露任何获取result的方法
      // 但是a实例的then方法里有个成功的回调函数,这个回调函数里可以取到result。
      result.then(this._resolve.bind(this), this._reject.bind(this));
      return;
    }
    this.status = MyPromise.Fulfiled;
    this.result = result;
    this.callbacks.forEach((cb) => {
      this._handle(cb);
    });
  }

  // 实现reject函数
  _reject(error) {
    if (error instanceof MyPromise) {
      error.then(this._resolve.bind(this), this._reject.bind(this));
    }
    this.status = MyPromise.Reject;
    this.error = error;
    this.callbacks.forEach((cb) => {
      this._handle(cb);
    });
  }

  // then方法把传入的回调函数保存起来,当状态改变时调用。
  then(onSuccess, onError) {
    // 把下一个Promise实例的resolve方法也存进callbasks中去
    // 是为了把当前onSuccess的执行结果通过resolve方法赋给下一个Promise实例的result
    return new MyPromise((nextResolve, nextReject) => {
      this._handle({
        nextResolve,
        nextReject,
        onSuccess,
        onError,
      });
    });
  }

  catch(onCatch) {
    return this.then(undefined, onCatch);
  }

  finally(onFinally) {
    return this.then(onFinally, onFinally);
  }

  _handle(cb) {
    const { nextResolve, nextReject, onSuccess, onError } = cb;

    if (this.status === MyPromise.Pending) {
      this.callbacks.push(cb);
    }

    if (this.status === MyPromise.Fulfiled) {
      const nextResult = onSuccess ? onSuccess(this.result) : undefined;
      nextResolve(nextResult);
      return;
    }

    if (this.status === MyPromise.Reject) {
      // 注意这里修改了
      const nextError = onError ? onError(this.error) : this.error;
      nextReject(nextError);
      return;
    }
  }
}

const p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject("Hello Promise");
  }, 1000);
});

// 调用第一个then方法之后,会创建下一个Promise对象。
// 会把当前Promise对象的成功回调函数、失败回调函数、下一个Resolve方法、下一个Reject方法存起来。
// 然后在调用onSuccess之后,把其返回值作为nextResolve的参数,赋给下一个Promise对象的result1,
// 这样第二个then方法的成功回调函数就会取到result1,以此类推。
p.then((res) => {
  console.log("res==========", res);
})
  .catch((err) => {
    console.log("err==========", err);
  })
  .finally(() => {
    console.log("finally");
  });

或许我如获至宝的感觉说出来太平缓了,那就再来看看JS实现的promise,代码冗长且难记,亲自做下对比

// 定义_Promise构造函数
function _Promise(fn) {
  this.status = "pending"; // 定义属性存储状态 // 赋予初始状态pending
  this.value = null; // resolve的value
  this.reason = null; // reject的reason
  this.onFulfilled = []; // 存储then方法中注册的第一个回调函数
  this.onReject = []; // 存储then方法中注册的第二个回调函数

  var handler = (funct) => {
    // 事件处理函数
    if (typeof funct === "function") {
      // 如果是函数的话才进行执行
      if (this.status === "fulfilled") funct(this.value); // 执行并传递value
      if (this.status === "rejected") funct(this.reason); // 执行并传递rejected
    }
  };

  // 实现resolve回调
  var resolve = (value) => {
    // 使用箭头函数主要是为了绑定this指向
    this.status = "fulfilled"; // 设置状态
    this.value = value; // 得到结果
    if (value instanceof _Promise) {
      // 判断返回的值是否为Promise实例
      value.onFulfilled = this.onFulfilled; // 是则以此链式调用
      return value;
    }
    setTimeout(() => {
      // 使用setTimeout是为了将回调函数置于任务队列,不阻塞主线程,异步执行,实际promise的回调是置于微队列的,而setTimeout的回调是置于宏队列
      try {
        this.onFulfilled.forEach(handler); // 交予事件处理
      } catch (e) {
        console.error(`Error in promise: ${e}`); // 打印异常
        reject(e); // reject
      }
    }, 0);
  };

  // 实现rejected
  var reject = (reason) => {
    // 使用箭头函数主要是为了绑定this指向
    this.status = "rejected"; // 设置状态
    this.reason = reason; // 得到结果
    setTimeout(() => {
      // 置于任务队列
      try {
        this.onReject.forEach(handler); // 交予事件处理
      } catch (e) {
        console.error(`Error in promise: ${e}`); // 打印异常
      }
    }, 0);
  };

  fn(resolve, reject); // 执行
}

// 定义then
// value接收上层结果 --- function处理自身逻辑 --- return传递到下层
_Promise.prototype.then = function (onFulfilled, onRejected) {
  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v; // 转为函数
  onRejected = typeof onRejected === "function" ? onRejected : (r) => r; // 转为函数
  return new _Promise((resolve, reject) => {
    // 返回一个新的_Promise
    this.onFulfilled.push((value) => {
      // 将回调函数置于onFulfilled
      resolve(onFulfilled(value)); // 执行并传递
    });
    this.onReject.push((value) => {
      // 将回调函数置于onReject
      reject(onRejected(value)); // 执行并传递
    });
  });
};
// 测试
var promise = new _Promise(function (resolve, reject) {
  var rand = Math.random() * 2;
  setTimeout(function () {
    if (rand < 1) resolve(rand);
    else reject(rand);
  }, 1000);
});
promise
  .then(
    (rand) => {
      console.log("resolve", rand); // resolve回调执行
    },
    (rand) => {
      console.log("reject", rand); // reject回调执行
    }
  )
  .then(function () {
    // resolve后继续执行
    return new _Promise(function (resolve, reject) {
      var rand = Math.random() * 2;
      setTimeout(function () {
        resolve(rand);
      }, 1000);
    });
  })
  .then(function (num) {
    // resolve后继续执行
    console.log(num, "继续执行并接收参数");
    return 1;
  })
  .then(function (num) {
    console.log(num, "继续执行并接收参数");
  });

/*
  实现的_Promise比较简单
  实际使用的Promise比较复杂,有各种情况的考虑
  例子中仅实现了Promise构造函数与then,实际中还有catch、Promise.all、Promise.race、Promise.resolve、Promise.reject等实现
 */

手动实现promise.all

这是我曾经面试头条的一道题目,归类记录下

const promiseAll=(arr)=>{
  let resInfo=[]
  return new Promise((resolve,reject)=>{
      let i=0
      next()
      next()=>{
        arr[i].then(re=>{
           resInfo.push(re)
           i++
           if(i===arr.length){
              resolve(resInfo)
           }else{
              next()
           }
        }
      }
  })
 }
 let p1=Promise.resolve(1)
 let p2=Promise.resolve('hello I am No2')
 let p3=Promise.resolve('hello I am No3')
 promiseAll([p1,p2,p3]).then(value=>{
    console.log(value)
 })

手动实现promiseAllSettled

  const promiseAllSettled=(arr)=>{
    if(Array.isArray(arr)){
       throw Error('must Array')
    }
    return new Promise((resolve)=>{
       let count=0
       let data=[]
       arr[i].then(res=>{
         data[i]={
            status:'fulfilled',
            value:res
         }
       },err=>{
           data[i]={
              status:'rejected',
              reason:err
           }
       })
       .finally(()=>{
          if(++count===len){
            resolve(data)
          }
       })
    })
  }
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值