深度理解Promise并简单实现

43 阅读6分钟

今日无事,勾栏听....

呸呸呸,今日无事,复习一下Promise吧

嘿,各位前端大佬们!今天咱们来复习下JavaScript里的Promise。Promise就像是咱们生活中的一个承诺,要么兑现(成功),要么食言(失败),而且一旦有了结果,就不会再改变啦。就好比你跟朋友约好一起吃饭,要么按时赴约(成功),要么放鸽子(失败),不会出现又赴约又放鸽子这种奇葩情况哈。

一、Promise的基本结构

咱们先来看一下Promise的基本结构,它是一个构造函数,接受一个执行器函数executor作为参数。这个执行器函数有两个参数,分别是resolvereject

class Promise {
  constructor(executor) {
    // 状态,一开始是pending(等待中)
    this.PromiseState = "pending";
    // 结果,初始为null
    this.PromiseResult = null;
    // 回调函数数组,用来存放后续的回调
    this.callbacks = [];

    const self = this;

    // 成功函数
    function resolve(value) {
      // 如果状态不是pending,就直接返回,因为Promise的状态一旦改变就不能再变啦
      if (self.PromiseState !== "pending") return;
      // 状态变为fulfilled(已成功)
      self.PromiseState = "fulfilled";
      // 保存成功的结果
      self.PromiseResult = value;
      // 如果有回调函数,就用setTimeout异步执行它们
      if (self.callbacks.length) {
        setTimeout(() => {
          self.callbacks.forEach((item) => {
            item.onResolved(value);
          });
        });
      }
    }

    // 失败函数
    function reject(value) {
      // 同样,如果状态不是pending,直接返回
      if (self.PromiseState !== "pending") return;
      // 状态变为rejected(已失败)
      self.PromiseState = "rejected";
      // 保存失败的结果
      self.PromiseResult = value;
      // 如果有回调函数,异步执行它们
      if (self.callbacks.length) {
        setTimeout(() => {
          self.callbacks.forEach((item) => {
            item.onRejected(value);
          });
        });
      }
    }

    // 同步调用执行器函数,如果执行器函数报错,就调用reject
    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
}

这里面有几个关键点哈:

  • PromiseState:表示Promise的状态,有三种可能:pending(等待中)、fulfilled(已成功)、rejected(已失败)。
  • PromiseResult:用来保存Promise的结果,不管是成功的结果还是失败的原因。
  • callbacks:一个数组,用来存放后续的回调函数。因为Promise可以链式调用,所以可能会有多个回调函数。
  • resolvereject:这两个函数是用来改变Promise状态的。resolve把状态变为fulfilledreject把状态变为rejected

二、Promise的then方法

then方法是Promise最常用的方法之一,它用来处理Promise的结果。then方法接受两个参数,分别是onResolvedonRejected,这两个参数都是函数。onResolved用来处理成功的结果,onRejected用来处理失败的原因。

Promise.prototype.then = function (onResolved, onRejected) {
  const self = this;
  // 如果onRejected不是函数,就给它一个默认的函数,这个函数会抛出错误
  if (typeof onRejected !== "function") {
    onRejected = (reason) => {
      throw reason;
    };
  }
  // 如果onResolved不是函数,就给它一个默认的函数,这个函数会返回传入的值
  if (typeof onResolved !== "function") {
    onResolved = (value) => {
      return value;
    };
  }

  return new Promise((resolve, reject) => {
    // 封装一个回调函数
    function callback(type) {
      try {
        // 执行传入的函数,得到结果
        const result = type(self.PromiseResult);
        // 如果结果是一个Promise,就调用它的then方法,根据结果调用resolve或reject
        if (result instanceof Promise) {
          result.then(
            (r) => {
              resolve(r);
            },
            (reason) => {
              reject(reason);
            }
          );
        } else {
          // 如果结果不是Promise,就直接调用resolve
          resolve(result);
        }
      } catch (error) {
        // 如果执行过程中报错,就调用reject
        reject(error);
      }
    }

    // 如果Promise的状态是fulfilled,就异步调用callback函数
    if (this.PromiseState === "fulfilled") {
      setTimeout(() => {
        callback(onResolved);
      });
    }
    // 如果Promise的状态是rejected,就异步调用callback函数
    if (this.PromiseState === "rejected") {
      setTimeout(() => {
        callback(onRejected);
      });
    }
    // 如果Promise的状态是pending,就把回调函数保存起来
    if (this.PromiseState === "pending") {
      this.callbacks.push({
        onResolved: function () {
          callback(onResolved);
        },
        onRejected: function () {
          callback(onRejected);
        },
      });
    }
  });
};

then方法的关键点:

  • 返回一个新的Promise:这样就可以实现链式调用啦。
  • 处理不同状态:根据Promise的状态,决定是立即执行回调函数还是保存回调函数。
  • 处理返回值:如果回调函数的返回值是一个Promise,就等待这个Promise的结果,再决定新Promise的状态。

三、Promise的其他方法

1. catch方法

catch方法其实就是then方法的一个语法糖,它只接受一个参数onRejected,用来处理Promise的失败情况。

Promise.prototype.catch = function (onRejected) {
  return this.then(undefined, onRejected);
};

2. resolve方法

resolve方法是一个静态方法,它可以把一个值或者一个Promise转换成一个Promise。

Promise.resolve = function (value) {
  return new Promise((resolve, reject) => {
    if (value instanceof Promise) {
      value.then(
        (v) => resolve(v),
        (r) => reject(r)
      );
    } else {
      resolve(value);
    }
  });
};

3. reject方法

reject方法也是一个静态方法,它可以把一个值或者一个Promise转换成一个被拒绝的Promise。

Promise.reject = function (value) {
  return new Promise((resolve, reject) => {
    if (value instanceof Promise) {
      value.then(
        (v) => reject(v),
        (r) => reject(r)
      );
    } else {
      reject(value);
    }
  });
};

4. all方法

all方法接受一个Promise数组作为参数,返回一个新的Promise。只有当数组中的所有Promise都成功时,新的Promise才会成功,返回值是一个数组,包含所有Promise的结果。如果有一个Promise失败,新的Promise就会失败,返回失败的原因。

Promise.all = function (promises) {
  return new Promise((resolve, reject) => {
    let fulfilledCount = 0;
    let arr = [];
    let rejected = false;
    for (let i = 0; i < promises.length; i++) {
      if (rejected) break;
      const promise = promises[i];
      if (promise instanceof Promise) {
        promise.then(
          (v) => {
            fulfilledCount++;
            arr[i] = v;
            if (promises.length === fulfilledCount) {
              resolve(arr);
            }
          },
          (r) => {
            rejected = true;
            reject(r);
          }
        );
      } else {
        fulfilledCount++;
        arr[i] = promise;
      }
    }
  });
};

5. race方法

race方法也接受一个Promise数组作为参数,返回一个新的Promise。只要数组中的有一个Promise有了结果(不管是成功还是失败),新的Promise就会有相同的结果。

Promise.race = function (promises) {
  return new Promise((resolve, reject) => {
    let resolved = false;
    let rejected = false;
    for (let i = 0; i < promises.length; i++) {
      if (resolved || rejected) break;

      const promise = promises[i];
      if (promise instanceof Promise) {
        promise.then(
          (v) => {
            resolved = true;
            resolve(v);
          },
          (r) => {
            rejected = true;
            reject(r);
          }
        );
      } else {
        resolve(promise);
        break;
      }
    }
  });
};

四、关键点

  1. Promise的状态是不可变的:一旦Promise的状态变为fulfilledrejected,就不能再改变了。这是Promise的一个重要特性,保证了结果的确定性。
  2. then方法返回的是一个新的Promise:这样就可以实现链式调用,每个then方法都可以处理上一个Promise的结果。
  3. catch方法可以捕获前面的错误:如果Promise链中有一个Promise失败了,后面的catch方法可以捕获这个错误。
  4. resolve和reject方法是静态方法:可以直接通过Promise.resolve()Promise.reject()来使用。
  5. all方法需要所有Promise都成功才会成功:只要有一个Promise失败,整个Promise.all就会失败。
  6. race方法只要有一个Promise有结果就会有结果:不管是成功还是失败,只要有一个Promise有了结果,Promise.race就会有相同的结果。
  7. Promise的回调函数是异步执行的:使用setTimeout来保证回调函数是异步执行的,避免了回调地狱。
  8. then方法的参数可以省略:如果只关心成功或失败的情况,可以只传一个参数给then方法。
  9. Promise可以链式调用多个then方法:每个then方法都可以处理上一个Promise的结果。
  10. Promise可以处理异步操作:比如网络请求、文件读取等,把异步操作封装成Promise,就可以用链式调用的方式来处理结果。

好啦,关于 Promise 就写到这里啦!希望这篇博客能让你对 Promise 有更深入的理解。如果有什么问题,那就多看几遍吧。所谓书读百遍其义自现。