大白话通俗易懂地手写 Promise

正常使用的 promise:

const lcPromise = new LcPromise((resolve, reject) => {
    
    
  resolve("resolve1");
  reject("reject1");
})

lcPromise.then(
  (res) => {
    
    
    console.log("res1: ", res);
  },
  (err) => {
    
    
    console.log("err1: ", err);
  }
);

lcPromise.then(
  (res) => {
    
    
    console.log("res2: ", res);
  },
  (err) => {
    
    
    console.log("err2: ", err);
  }
);

// 输出
// resolve1
// res1: resolve1
// res2: resolve1

基本结构

由上观察可知:

  1. Promsie 构造函数肯定接收一个参数函数
  2. 参数函数接收两个函数作为参数(resolve,reject)
  3. promise 拥有一个实例方法 then
  4. then 方法也接收两个参数函数(onFulfilled,onRejected)
  5. 根据Promise/A+ 规范,promise 有三种状态
  6. 规范规定,只有为 pending 状态,才能执行 resolve,reject,并且执行完更改对应状态
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;
    
    const resolve = value => {
    
    
      if (this.status === PROMISE_STATUS_PENDING) {
    
    
      	this.status = PROMISE_STATUS_FULFILLED
      }
    }
    const reject = reason => {
    
    
      if (this.status === PROMISE_STATUS_PENDING) {
    
    
        this.status = PROMISE_STATUS_REJECTED
      }
    }
    
    executed(resolve, reject)
  }
  
  then(onFulfilled, onRejected) {
    
    
    
  }
}

then 方法

我们知道执行 resolve 后就会执行 then 中的第一个参数函数,所以 then 中的参数函数调用肯定是在 resolve 函数的函数体中,这样才能做到 resolve 函数执行,然后 then 的参数函数跟着执行。
并且 then 可以多次调用,接收的多个参数函数可以依次执行,所以 then 接收的参数函数肯定被收集起来了
并且每个 then 的参数函数都能接收到 resolve 或者 reject 中的实参。

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;

    // 收集 then 的参数
    this.onFulfilledFns = []
    this.onRejectedFns = []
    
    const resolve = value => {
    
    
      if (this.status === PROMISE_STATUS_PENDING) {
    
    
      	this.status = PROMISE_STATUS_FULFILLED

        // 执行 then 中函数
        this.onFulfilledFns.forEach((fn) => fn(value));
      }
    }
    const reject = reason => {
    
    
      if (this.status === PROMISE_STATUS_PENDING) {
    
    
        this.status = PROMISE_STATUS_REJECTED

        this.onRejectedFns.forEach((fn) => fn(reason));
      }
    }
    
    executed(resolve, reject)
  }
  
  then(onFulfilled, onRejected) {
    
    
    this.onFulfilledFns.push(onFulfilled)
    this.onRejectedFns.push(onRejected)
  }
}

上述代码执行,会发现 resolve 执行时,onFulfilledFns 数组中并没有添加 then 中的参数函数,为什么?
因为 new 只会执行 constructor 方法,还没执行到 then 方法,resolve 方法就执行完了,自然就收集不到 then 的参数函数了。
所以现在目标是要让 then 方法先执行完毕,才去执行 resolve 中对 then 参数函数的调用。
可以使用事件循环,resolve 和 then是同步任务,将 resolve 中调用 then 参数函数的部分加入微任务队列,这样就能在 then 执行完收集后,再去调用了

const resolve = value => {
    
    
  if (this.status === PROMISE_STATUS_PENDING) {
    
    
    this.status = PROMISE_STATUS_FULFILLED
    // 加入微任务队列
    queueMicrotask(() => {
    
    
      // 执行 then 中函数
      this.onFulfilledFns.forEach((fn) => fn(value));
    })
      
  }
}
const reject = reason => {
    
    
  if (this.status === PROMISE_STATUS_PENDING) {
    
    
    this.status = PROMISE_STATUS_REJECTED
    queueMicrotask(() =>{
    
    
      this.onRejectedFns.forEach((fn) => fn(reason));
    })
  }
}

resolve 中 then 参数函数的调用加入了微队列,延迟在同步任务之后执行,解决了收集的问题。现在假如 then 函数本身在宏任务中呢?微任务先于宏任务,那宏任务中的 then 方法的参数就收集不到了。

// 宏任务中调用 then,resolve 无法收集到 then 的参数函数
setTimeout(() => {
    
    
  lcPromise.then(
    (res) => {
    
    
      console.log("res3: ", res);
    },
    (err) => {
    
    
      console.log("err3: ", err);
    }
  );
})

因此,宏任务中的 then 参数函数不能放在 resolve 中执行了。因为此时 resolve 已经执行完,所以在 resolve 执行完后的 then 参数都要在 then 中自己执行。
resolve 执行完,promise 状态变成了 fulfilled,所以在 fulfilled 后收集的参数都要自己执行。
并且参数函数需要拿到 resolve 的实参,之前参数函数在 resolve 中执行,可以拿到实参,现在参数函数在 resolve 外部执行,拿不到了,所以需要将实参保存到 promise 实例中共享

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;

    this.onFulfilledFns = []
    this.onRejectedFns = []

    this.value = undefined
    this.reason = undefined
    
    const resolve = value => {
    
    
      if (this.status === PROMISE_STATUS_PENDING) {
    
    
      	this.status = PROMISE_STATUS_FULFILLED
        this.value = value
        queueMicrotask(() => {
    
    
          this.onFulfilledFns.forEach((fn) => fn(value));
        })
          
      }
    }
    const reject = reason => {
    
    
      if (this.status === PROMISE_STATUS_PENDING) {
    
    
        this.status = PROMISE_STATUS_REJECTED
        this.reason = reason
        queueMicrotask(() =>{
    
    
          this.onRejectedFns.forEach((fn) => fn(reason));
        })
      }
    }
    
    executed(resolve, reject)
  }
  
  then(onFulfilled, onRejected) {
    
    
    // 1. 在 resolve 中执行
    this.onFulfilledFns.push(onFulfilled)
    this.onRejectedFns.push(onRejected)

    // 2. fulfilled 状态后收集的参数函数,在自身中执行
    if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
    
    
      onFulfilled(this.value)
    }
    if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
    
    
      onRejected(this.reason)
    }
  }
}

现在存在一个问题,先来看下测试代码的执行过程:

const lcPromise = new LcPromise((resolve, reject) => {
    
    
  resolve("resolve1");
  reject("reject1");
})

lcPromise.then(
  (res) => {
    
    
    console.log("res1: ", res);
  },
  (err) => {
    
    
    console.log("err1: ", err);
  }
);

setTimeout(() => {
    
    
  lcPromise.then(
    (res) => {
    
    
      console.log("res3: ", res);
    },
    (err) => {
    
    
      console.log("err3: ", err);
    }
  );
})

// 结果:res1 执行了两遍
// res1:  resolve1 
// res1:  resolve1
// res3:  resolve1
  1. new 构造函数,constructor 执行,参数函数 executed 开始执行,然后执行 resolve。
  2. 执行 resolve 的时候,判断是 pending 后立刻将状态改成了 fulfilled,然后将剩下部分加入微任务队列。
  3. 然后执行 reject,pending 判断不通过,跳出,constructor 同步任务执行完毕,开始执行实例方法 then。
  4. 第一个 then 调用开始执行,then 中将 res1、err1 加入收集数组,然后判断 promise 为 fulfilled,然后立即执行参数函数 res1、err1。问题就出在这里。

第一个then是同步任务调用,它的参数函数已经加入数组在 resolve 中等待微任务执行,可是现在在同步任务中立即执行了。它就执行了两遍。问题就出在 promise 的状态变早了,promise 的状态不能在同步任务中变成 fulfilled,因为 then 肯定是在 resolve 之后执行。promise 应该在微任务队列中改变状态,这样同步任务的 then 判断始终是 pending,就不会立即执行了。

const resolve = value => {
    
    
  if (this.status === PROMISE_STATUS_PENDING) {
    
    
    this.value = value
    queueMicrotask(() => {
    
    
      // 保持同步任务中,promise 状态为 pending
      this.status = PROMISE_STATUS_FULFILLED
      this.onFulfilledFns.forEach((fn) => fn(value));
    })
      
  }
}
const reject = reason => {
    
    
  if (this.status === PROMISE_STATUS_PENDING) {
    
    
    this.reason = reason
    queueMicrotask(() =>{
    
    
      this.status = PROMISE_STATUS_REJECTED
      this.onRejectedFns.forEach((fn) => fn(reason));
    })
  }
}

// 结果:then 中成功和失败的参数函数都执行了
// res1:  resolve1
// err1:  reject1
// err3:  reject1

但是这样还有一个问题:resolve 和 reject 都是同步任务,如果它们中的状态改变在微任务中,那就互相锁不住了。比如先执行 resolve ,将部分代码加入微任务队列后跳出,继续执行 reject,按理此时 reject 不应该执行有效代码,但是此时 promise 状态依然为 pending,没有锁住 reject,reject 正常执行,将其中的代码也加入了微任务队列。到时候就会看到 then 中的 res 参数函数和 err 参数函数都执行了。
因此,我们需要在微任务队列中通过判断 promise 的状态来互相阻止 resolve 和 reject 中的有效代码执行。

const resolve = value => {
    
    
  this.value = value
  queueMicrotask(() => {
    
    
    if (this.status !== PROMISE_STATUS_PENDING) return
    this.status = PROMISE_STATUS_FULFILLED
    this.onFulfilledFns.forEach((fn) => fn(value));
  })
}
const reject = reason => {
    
    
  this.reason = reason
  queueMicrotask(() =>{
    
    
    if (this.status !== PROMISE_STATUS_PENDING) return
    this.status = PROMISE_STATUS_REJECTED
    this.onRejectedFns.forEach((fn) => fn(reason));
  })
}

链式调用

const lcPromise = new LcPromise((resolve, reject) => {
    
    
  resolve("resolve1");
  reject("reject1");
})
  .then(
    (res) => {
    
    
      console.log("res1: ", res);
    },
    (err) => {
    
    
      console.log("err1: ", err);
    }
  )
  .then(
    (res) => {
    
    
      console.log("res2: ", res);
    },
    (err) => {
    
    
      console.log("err2: ", err);
    }
  );

为什么 then 可以链式调用?
因为 then 本身返回了一个 promise。所以我们也要在 then 中返回一个 promise。

then(onFulfilled, onRejected) {
    
    
  // 1. 在 resolve 中执行
  this.onFulfilledFns.push(onFulfilled);
  this.onRejectedFns.push(onRejected);

  // 2. fulfilled 状态后收集的参数函数,在自身中执行
  if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
    
    
    onFulfilled(this.value);
  }
  if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
    
    
    onRejected(this.reason);
  }

  return new LcPromise((resolve, reject) => {
    
    
    resolve("the second promise")
  })
}

现在 then 已经返回一个新的 promise 可以进行链式调用了,但是新 promise 中 resolve 的实参不应该是个定值。正常来讲,新 promise 中的 resolve 应该接收第一个 promise 中 then onFulfilled 参数函数的返回值。
为了让 resolve 拿到上一个 then 参数函数的返回值,可以把之前 then 中的代码全加入到第二个 promise 中,因为 constructor 都是同步执行,所以和在外部没啥区别。

  then(onFulfilled, onRejected) {
    
    
    return new LcPromise((resolve, reject) => {
    
    
      // 1. 在 resolve 中执行
      this.onFulfilledFns.push(onFulfilled);
      this.onRejectedFns.push(onRejected);
  
      // 2. fulfilled 状态后收集的参数函数,在自身中执行
      if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
    
    
        const value = onFulfilled(this.value);
        // resolve("the second promise")
        resolve(res)
  
      }
      if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
    
    
        const reason = onRejected(this.reason);
        reject(reason)
      }
    })
  }

上述代码有个严重的问题。正常情况下,前一个 then 中参数函数的返回值,无论是参数函数 onFulfilled 还是 onRejected 的返回值,它们都是被传入下一个 then 的 onFulfilled 中作为实参,也就是下一个 promise 中的 resolve 中作为实参。而不是 onFulfilled 与 resolve 对应,onRejected 与 reject 对应。只有上一个 then 中参数函数抛出的错误,才会传给下一个 promise 的 reject,也就是下一个 then 的 onRejected。
所以上述代码的 onRejected 中,不应该是 reject(reason),应该是 resolve(reason)。并且 onFulfilled 和 onRejected 都要被 try…catch 包裹,以将错误信息传给下一个 then 的 onRejected 中。

then(onFulfilled, onRejected) {
    
    
    return new LcPromise((resolve, reject) => {
    
    
      // 1. 在 resolve 中执行
      this.onFulfilledFns.push(onFulfilled);
      this.onRejectedFns.push(onRejected);

      // 2. fulfilled 状态后收集的参数函数,在自身中执行
      if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
    
    
        try {
    
    
          const value = onFulfilled(this.value);
          resolve(value);
        } catch (error) {
    
    
          reject(error);
        }
      }
      if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
    
    
        try {
    
    
          const reason = onRejected(this.reason);
          resolve(reason);
        } catch (error) {
    
    
          reject(error);
        }
      }
    });
  }

现在对那些在 then 自身中调用的参数函数已经处理好了,第二个 promise 的 resolve 很容易拿到它们的返回值,但那些被加入到数组中在第一个 promise 的 resolve 中执行的参数函数的返回值怎么办?
第一种办法是可以将它们的返回值保存出来共享,就像这样:

const resolve = (value) => {
    
    
  this.value = value
  queueMicrotask(() => {
    
    
    if (this.status !== PROMISE_STATUS_PENDING) return;
    this.status = PROMISE_STATUS_FULFILLED;
    this.firstPromiseValue = this.onFulfilledFns.forEach((fn) => fn(value));
  });

};
const reject = (reason) => {
    
    
  this.reason = reason;
  queueMicrotask(() => {
    
    
    if (this.status !== PROMISE_STATUS_PENDING) return;
    this.status = PROMISE_STATUS_REJECTED;
    this.firstPromiseReason = this.onRejectedFns.forEach((fn) => fn(reason));
  });
};

虽然可以,但是管理起来变量起来很麻烦。还有一个更巧妙的方式,不再直接将 then 的参数函数直接添加进数组中,而是将参数函数包裹一层函数后添加到数组中,然后在包裹函数的函数体中执行参数函数。这样相当于把参数函数的执行也留在了 then 自身中,添加到收集数组中的只是一个引线,resolve 中启动这根引线,开始执行参数函数。

// 1. 在 resolve 中执行
// this.onFulfilledFns.push(onFulfilled);
// this.onRejectedFns.push(onRejected);

this.onFulfilledFns.push(() => {
    
    
  try {
    
    
    const value = onFulfilled();
    resolve(value);
  } catch (error) {
    
    
    reject(error);
  }
});

this.onRejectedFns.push(() => {
    
    
  try {
    
    
    const reason = onRejected();
    resolve(reason);
  } catch (error) {
    
    
    reject(error);
  }
});

基本实现

const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;

    // 收集 then 的参数
    this.onFulfilledFns = [];
    this.onRejectedFns = [];

    this.value = undefined;
    this.reason = undefined;

    const resolve = (value) => {
    
    
      this.value = value;
      queueMicrotask(() => {
    
    
        if (this.status !== PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_FULFILLED;
        this.onFulfilledFns.forEach((fn) => fn(value));
      });
    };
    const reject = (reason) => {
    
    
      this.reason = reason;
      queueMicrotask(() => {
    
    
        if (this.status !== PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_REJECTED;
        this.onRejectedFns.forEach((fn) =>
          fn(reason)
        );
      });
    };

    executed(resolve, reject);
  }

  then(onFulfilled, onRejected) {
    
    
    return new LcPromise((resolve, reject) => {
    
    
      
      // 1. 在 resolve 中执行
      if (onFulfilled) this.onFulfilledFns.push(() => {
    
    
        try {
    
    
          const value = onFulfilled(this.value);
          resolve(value);
        } catch (error) {
    
    
          reject(error);
        }
      });

      if (onRejected) this.onRejectedFns.push(() => {
    
    
        try {
    
    
          const reason = onRejected(this.reason);
          resolve(reason);
        } catch (error) {
    
    
          reject(error);
        }
      });

      // 2. fulfilled 状态后收集的参数函数,在自身中执行
      if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
    
    
        try {
    
    
          const value = onFulfilled(this.value);
          resolve(value);
        } catch (error) {
    
    
          reject(error);
        }
      }
      if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
    
    
        try {
    
    
          const reason = onRejected(this.reason);
          resolve(reason);
        } catch (error) {
    
    
          reject(error);
        }
      }
    });
  }
}

测试:第二个 then 的 onFulfilled

const lcPromise = new LcPromise((resolve, reject) => {
    
    
  resolve("resolve1")
  reject("reject1")
})
  .then(
    (res) => {
    
    
      console.log("res1: ", res);
      return "aaa";
    },
    (err) => {
    
    
      console.log("err1: ", err);
      return "bbb"
    }
  )
  .then(
    (res) => {
    
    
      console.log("res2: ", res);
    },
    (err) => {
    
    
      console.log("err2: ", err);
    }
  );

// 结果
res1:  resolve1
res2:  aaa

// 结果2(注释 resolve("resolve1"),走第一个 then 的 onRejected)
err1:  reject1
res2:  bbb

测试:第二个 then 的 onRejected

const lcPromise = new LcPromise((resolve, reject) => {
    
    
  resolve("resolve1");
  reject("reject1");
})
  .then(
    (res) => {
    
    
      console.log("res1: ", res);
      throw 'hhh'
      return "aaa";
    },
    (err) => {
    
    
      console.log("err1: ", err);
      throw "ikun"
      return "bbb";
    }
  )
  .then(
    (res) => {
    
    
      console.log("res2: ", res);
    },
    (err) => {
    
    
      console.log("err2: ", err);
    }
  );

// 结果
res1:  resolve1
err2:  hhh

// 结果2 (注释 resolve("resolve1"),走第一个 then 的 onRejected)
err1:  reject1
err2:  ikun

上面重复的代码过多,可以抽取封装一下。

const execFunctionWithCatchError = (execFn, value, resolve, reject) => {
    
    
  try {
    
    
    const res = execFn(value)
    resolve(res)
  } catch (error) {
    
    
    reject(error)
  }
}
const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

const execFunctionWithCatchError = (execFn, value, resolve, reject) => {
    
    
  try {
    
    
    const res = execFn(value);
    resolve(res);
  } catch (error) {
    
    
    reject(error);
  }
};

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;

    // 收集 then 的参数
    this.onFulfilledFns = [];
    this.onRejectedFns = [];

    this.value = undefined;
    this.reason = undefined;

    const resolve = (value) => {
    
    
      this.value = value;
      queueMicrotask(() => {
    
    
        if (this.status !== PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_FULFILLED;
        this.onFulfilledFns.forEach((fn) => fn(this.value));
      });
    };
    const reject = (reason) => {
    
    
      this.reason = reason;
      queueMicrotask(() => {
    
    
        if (this.status !== PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_REJECTED;
        this.onRejectedFns.forEach((fn) => fn(this.reason));
      });
    };

    executed(resolve, reject);
  }

  then(onFulfilled, onRejected) {
    
    
    return new LcPromise((resolve, reject) => {
    
    
      // 1. 在 resolve 中执行
      if (onFulfilled) this.onFulfilledFns.push(() => {
    
    
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
      });

      if (onRejected) this.onRejectedFns.push(() => {
    
    
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
      });

      // 2. fulfilled 状态后收集的参数函数,在自身中执行
      if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
    
    
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
      }
      if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
    
    
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
      }
    });
  }
}

catch 方法

catch 方法并不是规范中的,只是 ES6 便于大家使用自己加的。
本来处理 promise 的 reject 是在 then 的第二个参数函数中,现在放在 then 链式调用的 catch 中。那我们可以手动将 then 拆成两部分执行。

catch(onRejected) {
    
    
  this.then(undefined, onRejected)
}
const lcPromise = new LcPromise((resolve, reject) => {
    
    
  reject("reject1");
})
  .then((res) => {
    
    
      console.log("res1: ", res);
    }, undefined)
  .catch((err) => {
    
    
    console.log("catch: ", err);
  });

但是上面这样其实是有问题的,
本来,reject 我们想要调用的是第一个 promise 的 onRejected 回调函数,但是因为 then 中第二个参数为 undefined,所以第一个 promise 的收集数组中是空的。现在用 catch 接收 onRejected 回调。
但是 catch 是链式调用的,真正调用 catch 的是第一个 then 返回的新 promise 实例。也就是 catch 中接收的 onRejected 实参函数被加到第二个 promise 的 onRejectedFns 数组中了

那通过第一个 promise 的 reject 怎么执行到第二个 promise 的 onRejected 回调呢?
第一种办法,肯定是将这个回调重新加到第一个 promise 的 onRejectedFns 数组中,这样行,但是非常麻烦。
第二种方法,不让第一个 promise reject 后,因为 then 的第二个参数为 undefined 就啥也不干,让第一个 promise 中 then 的 onRejected 回调和第二个 promise 中 then 的 onRejected 回调也形成链式调用一样。相当于第一个 promise 中的 onRejected 在发送信号,我被执行了,第二个 promise 中真正的我赶紧执行。
前面我们已经知道,只有一种方式才能让第二个 promise 的 onRejected 执行,就是第一个 promise 抛出异常。
所以我们可以通过当 then 的第二个参数为 undefined 时,就添加一个抛出异常的函数进入收集数组中,等待执行,从而启动第二个 promise 中真正的本体执行。

then(onFulfilled, onRejected) {
    
    
  // 当 onRejected 为 undefined 或其他空值时,就将它替换为一个抛出异常的函数
  onRejected = onRejected || ((err) => {
    
     throw err });
  return new LcPromise() {
    
    ...}
}

catch(onRejected) {
    
    
  this.then(undefined, onRejected)
}

finally 方法

无论 promise 是成功还是失败都要执行 finally 方法,由上可知,promise 实际执行的都是 then 方法。所以 finally 的实现无非是这样:

finally(fns) {
    
    
  this.then(fns, fns)
}
const lcPromise = new LcPromise((resolve, reject) => {
    
    
  resolve("resolve1");
})
  .then((res) => {
    
    
    console.log("res1: ", res);
    return "aaa"
  })
  .catch((err) => {
    
    
    console.log("catch: ", err);
  })
  .finally(() => {
    
    
    console.log("finally");
  });

finally 也是链式调用的,所以 catch 要将第二个 promise 返回。

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

finally 也会碰到 catch 一样的问题:调用链断掉。这次断在 catch 的 this.(undefined, onRejected)的 undefined 中。
如上述代码调用,当第一个 promise resolve,第一个 then 中的 onFulfilled 执行,并且拥有返回值。此时就会启动第二个 promise then 中的 onFulfilled 执行,也就是 catch 中的this.(undefined, onRejected)执行,但是此时 onFulfilled 为 undefined,调用链断了,无法启动第三个 promise 的 then 执行了,也就是无法启 finally 的this.then(fns, fns)执行,也就是 finally 无法执行。
因此** catch 中的 onFulfilled 参数函数不能为 undefined,它需要是一个有返回值的任意函数,用来启动后续 onFulfilled 执行**。catch 执行的也是 then,所以根上是 then 方法的 onFulfilled 不能为 undefined,改 then 就行。

then(onFulfilled, onRejected) {
    
    
  // 当 onRejected 为 undefined 或其他空值时,就将它替换为一个抛出异常的函数
  onRejected = onRejected || ((err) => {
    
     throw err });

  // 当 onFulfilled 为 undefined 或其他空值时,就将它替换为一个有返回值的函数
  onFulfilled = onFulfilled || ((value) => value);
  
  return new LcPromise() {
    
    ...}
}

catch(onRejected) {
    
    
  return this.then(undefined, onRejected)
}

完整实现

const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

const execFunctionWithCatchError = (execFn, value, resolve, reject) => {
    
    
  try {
    
    
    const res = execFn(value);
    resolve(res);
  } catch (error) {
    
    
    reject(error);
  }
};

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;

    // 收集 then 的参数
    this.onFulfilledFns = [];
    this.onRejectedFns = [];

    this.value = undefined;
    this.reason = undefined;

    const resolve = (value) => {
    
    
      this.value = value;
      queueMicrotask(() => {
    
    
        if (this.status !== PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_FULFILLED;
        this.onFulfilledFns.forEach((fn) => fn(this.value));
      });
    };
    const reject = (reason) => {
    
    
      this.reason = reason;
      queueMicrotask(() => {
    
    
        if (this.status !== PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_REJECTED;
        this.onRejectedFns.forEach((fn) => fn(this.reason));
      });
    };

    executed(resolve, reject);
  }

  then(onFulfilled, onRejected) {
    
    
    // 当 onRejected 为 undefined 或其他空值时,就将它替换为一个抛出异常的函数
    onRejected =
      onRejected ||
      ((err) => {
    
    
        throw err;
      });

    // 当 onFulfilled 为 undefined 或其他空值时,就将它替换为一个有返回值的函数
    onFulfilled = onFulfilled || ((value) => value);
    return new LcPromise((resolve, reject) => {
    
    
      // 1. 在 resolve 中执行
      if (onFulfilled)
        this.onFulfilledFns.push(() => {
    
    
          execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
        });

      if (onRejected)
        this.onRejectedFns.push(() => {
    
    
          execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
        });

      // 2. fulfilled 状态后收集的参数函数,在自身中执行
      if (this.status === PROMISE_STATUS_FULFILLED && onFulfilled) {
    
    
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
      }
      if (this.status === PROMISE_STATUS_REJECTED && onRejected) {
    
    
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
      }
    });
  }

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

  finally(fns) {
    
    
    this.then(fns, fns);
  }
}

猜你喜欢

转载自blog.csdn.net/qq_43220213/article/details/129761755