返回

深入浅出剖析Promise源代码,彻底掌握异步编程精髓

前端

在当今快速发展的互联网时代,异步编程已成为Web开发中不可或缺的一部分。它使我们能够在不阻塞主线程的情况下执行耗时操作,从而显著提高Web应用程序的性能和用户体验。在众多异步编程技术中,Promise无疑是最受欢迎和最强大的工具之一。

Promise是一个JavaScript对象,它代表着异步操作的最终完成或失败。它提供了一种简单而优雅的方式来处理异步操作,使代码更易于阅读和维护。

为了深入理解Promise的机制,我们不妨自己动手实现一个简单的Promise。这将有助于我们更好地理解Promise的内部工作原理,以及如何在实际开发中使用它。

首先,我们创建一个构造函数Promise,它接受一个函数作为参数。这个函数有两个参数,分别是resolve和reject。resolve用于表示异步操作成功完成,而reject则用于表示异步操作失败。

function Promise(executor) {
  // 当前Promise的状态
  this.state = 'pending';

  // 成功时要执行的回调函数队列
  this.onFulfilledCallbacks = [];

  // 失败时要执行的回调函数队列
  this.onRejectedCallbacks = [];

  // 执行executor函数,并传入resolve和reject函数
  executor(resolve, reject);
}

接下来,我们定义resolve和reject函数。resolve函数将把Promise的状态从“pending”改为“fulfilled”,并执行所有注册的成功回调函数。reject函数则将把Promise的状态从“pending”改为“rejected”,并执行所有注册的失败回调函数。

function resolve(value) {
  // 如果Promise的状态不是“pending”,则直接返回
  if (this.state !== 'pending') {
    return;
  }

  // 将Promise的状态改为“fulfilled”
  this.state = 'fulfilled';

  // 依次执行所有注册的成功回调函数
  this.onFulfilledCallbacks.forEach(callback => {
    callback(value);
  });
}

function reject(reason) {
  // 如果Promise的状态不是“pending”,则直接返回
  if (this.state !== 'pending') {
    return;
  }

  // 将Promise的状态改为“rejected”
  this.state = 'rejected';

  // 依次执行所有注册的失败回调函数
  this.onRejectedCallbacks.forEach(callback => {
    callback(reason);
  });
}

然后,我们定义then方法。then方法接受两个参数,分别是成功的回调函数和失败的回调函数。它返回一个新的Promise,表示异步操作的最终结果。

Promise.prototype.then = function(onFulfilled, onRejected) {
  // 如果onFulfilled不是函数,则将其设置为一个默认的空函数
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;

  // 如果onRejected不是函数,则将其设置为一个默认的空函数
  onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };

  // 创建一个新的Promise
  const newPromise = new Promise((resolve, reject) => {
    // 如果当前Promise的状态是“fulfilled”,则执行成功的回调函数
    if (this.state === 'fulfilled') {
      setTimeout(() => {
        try {
          const result = onFulfilled(this.value);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      }, 0);
    }

    // 如果当前Promise的状态是“rejected”,则执行失败的回调函数
    if (this.state === 'rejected') {
      setTimeout(() => {
        try {
          const reason = onRejected(this.reason);
          resolve(reason);
        } catch (error) {
          reject(error);
        }
      }, 0);
    }

    // 如果当前Promise的状态是“pending”,则将成功的回调函数和失败的回调函数添加到相应的队列中
    if (this.state === 'pending') {
      this.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            const result = onFulfilled(this.value);
            resolve(result);
          } catch (error) {
            reject(error);
          }
        }, 0);
      });

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

  // 返回新的Promise
  return newPromise;
};

最后,我们定义catch方法。catch方法与then方法类似,但它只接受一个失败的回调函数。它返回一个新的Promise,表示异步操作的最终结果。

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

至此,我们已经完成了Promise的实现。现在,我们可以使用它来处理异步操作。例如,我们可以在一个函数中使用Promise来获取一个远程资源。

function fetchRemoteResource(url) {
  return new Promise((resolve, reject) => {
    // 使用XMLHttpRequest来获取远程资源
    const xhr = new XMLHttpRequest();

    xhr.open('GET', url);

    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(xhr.responseText);
      } else {
        reject(new Error('请求失败'));
      }
    };

    xhr.onerror = () => {
      reject(new Error('请求失败'));
    };

    xhr.send();
  });
}

然后,我们可以在另一个函数中使用then方法来处理获取远程资源的结果。

fetchRemoteResource('https://www.example.com/resource.json')
  .then(data => {
    // 使用数据
  })
  .catch(error => {
    // 处理错误
  });

通过这种方式,我们可以使用Promise来处理各种异步操作,从而使我们的代码更易于阅读和维护。