返回

手写Promise:从菜鸟到进坑的实战指南

前端

手写Promise从入门到入坑

前言

作为一名开发小白,我曾天真地以为,Promise不过是异步编程的垫脚石,轻轻松松就能掌握。然而,当自己动手尝试编写Promise时,才发现这远比想象中困难。从早到晚地肝代码,却始终不得要领。这不禁让我感慨:我终究还是太菜了。

从零开始

什么是Promise?

Promise是一个表示异步操作结果的特殊对象。当异步操作成功时,Promise会以成功的值resolve,当操作失败时,Promise会以失败的原因reject。

Promise的链式调用

Promise最强大的特性之一是链式调用,它可以串联多个异步操作,并根据前一个操作的结果决定后续操作。链式调用通过then方法实现,它返回一个新的Promise对象,代表后续操作的结果。

手写Promise的历程

第一步:实现基础Promise

手写Promise的第一步是实现一个基础的Promise类,它包含了resolve和reject两个方法。

class Promise {
  constructor(executor) {
    this.status = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (e) {
      this.reject(e);
    }
  }

  resolve(value) {
    if (this.status !== 'pending') return;
    this.status = 'fulfilled';
    this.value = value;
    this.onFulfilledCallbacks.forEach(callback => callback(value));
  }

  reject(reason) {
    if (this.status !== 'pending') return;
    this.status = 'rejected';
    this.reason = reason;
    this.onRejectedCallbacks.forEach(callback => callback(reason));
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

    const promise2 = new Promise((resolve, reject) => {
      if (this.status === 'fulfilled') {
        setTimeout(() => {
          try {
            const x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === 'rejected') {
        setTimeout(() => {
          try {
            const x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              const x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    reject(new TypeError('Chaining cycle detected for promise!'));
  }

  if (x instanceof Promise) {
    x.then(y => resolvePromise(promise2, y, resolve, reject), reason => reject(reason));
  } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      const then = x.then;
      if (typeof then === 'function') {
        then.call(x, y => resolvePromise(promise2, y, resolve, reject), reason => reject(reason));
      } else {
        resolve(x);
      }
    } catch (e) {
      reject(e);
    }
  } else {
    resolve(x);
  }
}

第二步:模拟链式调用

实现了基础Promise后,接下来需要模拟链式调用。链式调用的本质是将前一个操作的结果作为后一个操作的参数。

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('成功');
  }, 1000);
});

promise1.then(result => {
  console.log(result); // 输出:成功
});

第三步:遇到困难

当代码写到这一步时,我遇到了困难。如何处理多个then操作?如何保证then操作的顺序执行?如何处理异常情况?一系列的问题接踵而至。

我尝试了各种方法,但始终无法得到理想的结果。于是,我开始怀疑自己的能力,觉得自己或许根本不适合写代码。

请教高手

就在我濒临绝望之际,我决定向高手求助。我将代码发给了一位经验丰富的程序员朋友,请他指点一二。

朋友耐心地审阅了我的代码,指出其中的问题所在。原来,我在处理多个then操作和保证顺序执行方面存在缺陷。

在朋友的指导下,我修改了代码,终于实现了链式调用的功能。

结语

手写Promise的过程是一段艰辛的旅程。从一开始的信心满满到后来的怀疑自我,再到最后的恍然大悟,这一路走来,我经历了太多。

虽然最终成功手写了Promise,但我并没有因此而自满。我知道,我还有很多需要学习的。这次经历让我深刻地意识到,编程是一门需要不断学习和积累的学问。

对于那些和我一样还在学习编程的小伙伴,我只有一句忠告:不要害怕失败,不断学习,终有一天,你也会成为一名优秀的程序员。