Promise介绍及手写Promise的所有属性方法

921 阅读5分钟

最近看了3天Promise,终于把Promise看懂了。以下为自己整理的Promise介绍和如何实现Promise的then/catch/resolve/reject及所有Promsie方法。(本文适合有一定js基础的人观看,代码详细解释有空我再补充) 以及Promise.all/Promise.race/Promise.allSettled/Promsie.any/Promise.retry的实现方法。

Promise介绍

特点:

是个构造函数,接收一个函数类型的参数。原型链上存在then/catch方法,then/catch方法也返回promise。支持异步执行,支持链式调用。

Promise对象有三种状态:

pending进行中、fulfilled已成功、rejected已失败

then方法:

promise.then((value) => {}, (error) => {})   可接收两个回调函数作为参数;第一个回调是Promise对象状态变为resolved时调用,第二个回调是状态变为rejected时调用;这俩函数都是可选的。

catch方法:

Promise.prototype,catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名。

Promise.all([p1, p2, p3]):

都成功才会成功,有一个失败就会变成rejected

Promise.race()  :

哪个最快就返回哪个的结果,不管别的执没执行完成

Promise.allSettled() ES2020(ES11)

所有请求都结束。参数数组的所有Promise对象都发生状态变更(不管是fulfilled还是rejected),返回的Promise对象才会发生状态变更。 该方法返回新的Promise实例,一旦发生状态变更,状态总是fulfilled,不会变成rejected。 返回结果: 异步操作成功{status: 'fulfilled', value: value};异步操作失败{status: 'rejected', reason: reason}      Promise.allSettled([p1, p2, p3]).then(res => { console.log('res=', res) }) // [{status: 'rejected', reason: 11}, ......]

ES2021: Promise.any()

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态,若参数实例都变成rejected状态,包装实例就会变成rejected状态。 和Promise.race不同点:不会因为某个promise变成rejected状态而结束,必须等到所有promise变成rejected状态才会结束。 总结:有一个成功就会成功,所有失败才会失败 AggregateError: All promises were rejected

手写Promise及相关内容

function MyPromise() {  }
Promise.resolve() / Promise.reject()
MyPromise.prototype.then = function () { }
MyPromise.prototype.catch = function () { }
MyPromise.prototype.all = function () { }
MyPromise.prototype.race= function () { }
MyPromise.prototype.allSettled= function () { }
MyPromise.prototype.any= function () { }
MyPromise.prototype.retry= function () { } (扩展)

实现一个Promise流程

  • 第一步:依然是执行new Promise中的同步代码,我们加了两个数组 that.onfufilledCallback = [];that.onrejectedCallback = [];来缓存then函数的handleFulfilled方法, handleRejected方法
  • 第二步:执行 executor(resolve, reject);的时候遇到setTimeout,那就先等等再来执行setTimeout里面的东西吧
  • 第三步:执行第一个then吧
  • 第四步:在then函数里面有new MyPromise执行吧
  • 第五步:执行then函数中的executor(resolve, reject),同步执行当前的状态是pending,并且将当前handleFulfilled, handleRejected缓存了下来
  • 第六步:执行第二个then函数,操作过程同上一个then,依次将第二个then的handleFulfilled缓存下来
  • 第七步:第三个then执行,缓存

实现一个Promise

function MyPromise(executor) {
  this.status = "pending"; //初始状态
  this.success = undefined; // 成功的值
  this.error = undefined; // 失败的原因

  this.onfufilledCallback = [];
  this.onrejectedCallback = [];

  let resolve = (val) => {
    if (val instanceof MyPromise) {
      return val.then(resolve, reject);
    }
    queueMicrotask(() => {
      // 模拟异步任务
      if (this.status == "pending") {
        this.status = "fufilled";
        this.success = val; //保存当前成功的返回值
        this.onfufilledCallback.forEach((resolvefn) => {
          resolvefn(this.success);
        });
      }
    });
  };
  let reject = (val) => {
    queueMicrotask(() => {
      if (this.status == "pending") {
        this.status = "rejected";
        this.error = val;
        this.onrejectedCallback.forEach((rejectfn) => {
          rejectfn(this.error);
        }); //同步执行的时候缓存
      }
    });
  };
  try {
    executor(resolve, reject);
  } catch (err) {
    reject(err);
  } // 立即执行;若executor执行报错,直接执行reject
}

promise.then实现

MyPromise.prototype.then = function (handleFulfilled, handleRejected) {
  handleFulfilled =
    typeof handleFulfilled == "function"
      ? handleFulfilled
      : (data) => {
          return data;
        }; // 解决promise穿透现象
  handleRejected =
    typeof handleRejected == "function"
      ? handleRejected
      : (data) => {
          throw data;
        };
  let that = this;
  let mypromise2 = new MyPromise((resolve, reject) => {
    if (that.status == "fufilled") {
      try {
        let res = handleFulfilled(this.success); //成功的值
        resolve(res); //不管上一个then是成功还是失败第二个then都可以接收到上面返回的值
      } catch (e) {
        reject(e);
      }
    }
    if (that.status == "rejected") {
      try {
        let err = handleRejected(this.error); //失败的值
        resolve(err); //不管上一个then是成功还是失败第二个then都可以接收到上面返回的值
      } catch (e) {
        reject(e);
      }
    }
    if (that.status == "pending") {
      //当函数调用的时候,去执行数组里面的函数
      that.onfufilledCallback.push(() => {
        try {
          let res = handleFulfilled(this.success);
          resolve(res); //不管上一个then是成功还是失败第二个then都可以接收到上面返回的值
        } catch (e) {
          reject(e);
        }
      }); //这里的onfufulled就相当于then里面的第一个参数回调
      that.onrejectedCallback.push(() => {
        try {
          let res = handleRejected(this.error);
          resolve(res); //不管上一个then是成功还是失败第二个then都可以接收到上面返回的值
        } catch (e) {
          reject(e);
        }
      });
    }
  });
  return mypromise2;
};

promise.catch实现

MyPromise.prototype.catch = function (callback) {
  return this.then(function () { }, callback)
}

Promise.resolve实现

Promise.resolve = function (param) {
  if (param instanceof MyPromise) {
    // 如果传入的是个promise类型,直接执行
    return param;
  }
  return new MyPromise((resolve) => resolve(param));
};

Promise.reject实现

MyPromise.reject = (reason) => {
    return new MyPromise((resolve, reject) => reject(reason))
}

Promise.all([p1, 22, p3])实现

MyPromise.all = function (values) {
  let num = 0;
  let arr = [];
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < values.length; i++) {
      let current = values[i];
      if (current.then && typeof current.then === "function") {
        // 传入的是promise,要执行promise并返回结果
        current.then.call(
          current,
          (val) => {
            arr[i] = val; // 跟数组传参顺序一致
            if (++num === values.length) {
              // 考虑异步
              resolve(arr);
            }
          },
          reject
        );
      } else {
        // 传入的是具体的值
        arr[i] = current;
        if (++num === values.length) {
          resolve(arr);
        }
      }
    }
  });
};
使用示例:
let promise1 = new MyPromise((resolve, reject) => {
    setTimeout(() => { resolve(111) }, 2000)
})
let promise3 = new MyPromise((resolve, reject) => {
    setTimeout(() => { resolve(3333) }, 2000)
})
MyPromise.all([promise1, 22, promise3]).then(res => { console.log('res==', res) })

Promise.race([p1,222,p3]) 执行完一个得到一个状态就输出

MyPromise.race = function (values) {
  return new MyPromise(function (resolve, reject) {
    try {
      let num = 0;
      let arr = [];
      let current;
      for (let i = 0; i < values.length; i++) {
        current = values[i];
        if (current.then && typeof current.then === "function") {
          current.then(resolve, reject);
        } else {
          resolve(current); // 若是普通值,直接抛出
        }
      }
    } catch (e) {
      reject(e);
    }
  });
};

Promise.allsettled([]):  所有参数都执行完 [{status: 'rejected', reason: 11}, ......]

Promise.myAllSettled = function (values) {
  let num = 0;
  let arr = [];
  return new Promise((resolve, reject) => {
    for (let i = 0; i < values.length; i++) {
      Promise.resolve(values[i]).then(
        (res) => {
          arr[i] = { status: "fulfilled", value: res };
          if (++num === values.length) {
            resolve(arr);
          }
        },
        (err) => {
          arr[i] = { status: "rejected", value: err };
          if (++num === values.length) {
            resolve(arr);
          }
        }
      );
    }
  });
};

**Promise.any([])  ** 全失败才会失败AggregateError: All promises were rejected,有一个成功就会成功

Promise.myAny = function (values) {
  let num = 0;
  let arr = [];
  for (let i = 0; i < values.length; i++) {
    Promise.resolve(values[i])
      .then((res) => {
        resolve(res);
      })
      .catch((err) => {
        arr[i] = err;
        if (++num === values.length) {
          reject("AggregateError: All promises were rejected");
        }
      });
  }
};

Promise的retry(重试)功能实现:一个方法失败后间隔delay秒再重试,重试超过times次后,报错

Promise.retry = (getData, times, delay) => {
  return new Promise((resolve, reject) => {
    function attemp() {
      getData()
        .then(resolve)
        .catch((err) => {
          if (times === 0) {
            reject(err);
          } else {
            times--;
            setTimeout(attemp(), delay);
          }
        });
    }
    attempt();
  });
};

另附一道面试题:

MyPromise.resolve().then(() => {
  console.log(0);
  return MyPromise.resolve(4);
}).then((res) => {
  console.log(res)
})
MyPromise.resolve().then(() => {
  console.log(1);
}).then(() => {
  console.log(2);
}).then(() => {
  console.log(3);
}).then(() => {
  console.log(5);
}).then(() =>{
  console.log(6);
})  // 输出:0124356   若换成原生Promise,则0123456

原因:js引擎为了让微任务尽快输出,做了一些优化,连续多个then(3个)若没有reject或resolve,会交替执行then,而不至于让一个堵太久,不单单v8这样其他引擎也这样,其实promise内部状态已经结束。这块在v8源码里有完成体现。