JavaScript中手写promise ?

参考回答:

手写一个简单的 Promise 实现可以帮助理解其内部机制。一个 Promise 主要有三个状态:pendingresolved(fulfilled)和 rejected。我们需要模拟这些状态变化以及相应的 then()catch() 方法。

详细讲解与拓展:

下面是一个简化版的 Promise 实现:

class MyPromise {
  constructor(executor) {
    // 初始化状态
    this.state = 'pending';  // 'pending' | 'fulfilled' | 'rejected'
    this.value = undefined;  // 存储成功的值
    this.reason = undefined; // 存储失败的原因
    this.onFulfilledCallbacks = []; // 存储成功的回调
    this.onRejectedCallbacks = [];  // 存储失败的回调

    // resolve 方法,用来改变状态为 fulfilled,并返回值
    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        // 执行所有成功回调
        this.onFulfilledCallbacks.forEach(fn => fn(value));
      }
    };

    // reject 方法,用来改变状态为 rejected,并返回原因
    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        // 执行所有失败回调
        this.onRejectedCallbacks.forEach(fn => fn(reason));
      }
    };

    // 执行传入的 executor 函数
    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error); // 如果 executor 中有异常,调用 reject
    }
  }

  // then 方法,返回一个新的 Promise
  then(onFulfilled, onRejected) {
    // 如果 onFulfilled 不是函数,默认返回传入的值
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    // 如果 onRejected 不是函数,默认抛出错误
    onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };

    // 返回一个新的 Promise
    return new MyPromise((resolve, reject) => {
      // 处理 Promise 状态变化后的回调
      if (this.state === 'fulfilled') {
        // 如果 Promise 已经是 fulfilled 状态,立即调用 onFulfilled
        setTimeout(() => {
          try {
            const result = onFulfilled(this.value);
            resolve(result);
          } catch (error) {
            reject(error);
          }
        });
      } else if (this.state === 'rejected') {
        // 如果 Promise 已经是 rejected 状态,立即调用 onRejected
        setTimeout(() => {
          try {
            const result = onRejected(this.reason);
            resolve(result);
          } catch (error) {
            reject(error);
          }
        });
      } else {
        // 如果 Promise 还处于 pending 状态,保存回调,待状态变化时调用
        this.onFulfilledCallbacks.push(() => {
          setTimeout(() => {
            try {
              const result = onFulfilled(this.value);
              resolve(result);
            } catch (error) {
              reject(error);
            }
          });
        });

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

  // catch 方法,处理 Promise 的 reject 状态
  catch(onRejected) {
    return this.then(null, onRejected);
  }
}

解释:

  1. constructor(executor):构造函数接受一个 executor 函数,它有两个参数:resolvereject。当异步操作成功时,调用 resolve(value);失败时,调用 reject(reason)
    • this.state:表示 Promise 的状态,可以是 'pending'(待定),'fulfilled'(已解决),或者 'rejected'(已拒绝)。
    • this.valuethis.reason:分别存储成功的结果和失败的原因。
    • this.onFulfilledCallbacksthis.onRejectedCallbacks:分别用于存储 then 方法中的成功回调和失败回调。
  2. then(onFulfilled, onRejected)then 方法会返回一个新的 Promise,这是 Promise 链式调用的关键。它接受两个回调函数,分别处理成功和失败的结果。如果当前 Promise 已经是 fulfilledrejected 状态,立即调用相应的回调。如果是 pending 状态,则将回调存储起来,等待状态改变时执行。

  3. catch(onRejected)catch 方法是 then 方法的一个简写,用于处理失败的情况,它实际上调用的是 then(null, onRejected)

使用示例:

const myPromise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve('Success');
  }, 1000);
});

myPromise.then((value) => {
  console.log(value);  // 输出 'Success'
  return 'Another success';
}).then((value) => {
  console.log(value);  // 输出 'Another success'
}).catch((error) => {
  console.log(error);
});

详细讲解:

  • 状态变化:一旦 Promise 被解析为 fulfilledrejected,状态就无法再更改,因此在 resolvereject 中,我们只允许在 pending 状态下修改状态。
  • 回调队列:当 Promise 仍处于 pending 状态时,then() 会将回调函数加入队列。状态变化时,回调函数会被按顺序执行。
  • setTimeout:在 then()catch() 中使用 setTimeout 来确保回调函数在事件循环的下一轮执行,以便模拟异步行为。

总结:

手写 Promise 是理解异步操作和 Promise 工作原理的一个好方法。通过实现基本的 resolverejectthen()catch() 方法,我们能够模拟异步操作并处理回调链。这个实现虽然简单,但已经涵盖了 Promise 的基本功能。在实际应用中,我们通常使用内建的 Promise,而手写实现有助于深入理解异步编程的核心概念。

发表评论

后才能评论