返回

从Promise基础到手写,亲手打造异步编程神器

前端

前言

在现代Web开发中,异步编程已经成为一种常态。为了处理异步操作,JavaScript提供了一系列的API,其中Promise就是其中之一。Promise是一种用于处理异步操作的强大工具,它可以使代码更加清晰易读,并避免回调地狱。

Promise简介

Promise最早就出现时是为了解决编程中的异步行为导致的回调地狱。在没有Promise之前,对于函数的异步行为,一般采用回调函数的方式,在一个函数调用结束触发回调函数,这样会导致代码非常难以阅读和维护。为了解决这个问题,Promise应运而生。

Promise是一个对象,它表示一个异步操作的最终完成(或失败)及其结果值。我们可以通过Promise来跟踪异步操作的状态,并在操作完成后执行相应的操作。

Promise的基本用法

使用Promise非常简单,我们只需要按照以下步骤操作即可:

  1. 创建一个Promise对象,这个对象表示一个异步操作。
  2. 在Promise对象上调用then()方法,传入两个函数作为参数,这两个函数分别是成功回调函数和失败回调函数。
  3. 当异步操作成功完成后,Promise对象就会调用成功回调函数,并将操作结果作为参数传递给该函数。
  4. 如果异步操作失败,Promise对象就会调用失败回调函数,并将错误信息作为参数传递给该函数。

以下是一个使用Promise的简单示例:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Hello, Promise!');
  }, 2000);
});

promise.then((result) => {
  console.log(result); // 输出: Hello, Promise!
});

在这个示例中,我们首先创建了一个Promise对象,并在构造函数中传入了一个函数作为参数。这个函数有两个参数,分别是resolve和reject。resolve表示异步操作成功,reject表示异步操作失败。

然后,我们调用Promise对象的then()方法,传入两个函数作为参数。第一个函数是成功回调函数,当异步操作成功完成后,Promise对象就会调用这个函数,并将操作结果作为参数传递给该函数。第二个函数是失败回调函数,如果异步操作失败,Promise对象就会调用这个函数,并将错误信息作为参数传递给该函数。

最后,我们调用setTimeout()方法来模拟一个异步操作。2秒后,我们调用resolve()方法来表示异步操作成功,并将'Hello, Promise!'作为操作结果传递给该方法。

Promise的链式调用

Promise支持链式调用,这使得我们可以将多个异步操作连接起来,形成一个异步操作队列。当一个异步操作完成后,下一个异步操作就会自动执行。

以下是一个使用Promise链式调用的示例:

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Hello, Promise!');
  }, 2000);
});

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('I am Promise!');
  }, 1000);
});

promise1.then((result) => {
  console.log(result); // 输出: Hello, Promise!
  return promise2;
}).then((result) => {
  console.log(result); // 输出: I am Promise!
});

在这个示例中,我们首先创建了两个Promise对象,promise1和promise2。然后,我们调用promise1的then()方法,传入两个函数作为参数。第一个函数是成功回调函数,当promise1成功完成后,Promise对象就会调用这个函数,并将操作结果作为参数传递给该函数。第二个函数是失败回调函数,如果promise1失败,Promise对象就会调用这个函数,并将错误信息作为参数传递给该函数。

在成功回调函数中,我们返回promise2。这表示当promise1成功完成后,下一个异步操作就是promise2。然后,我们调用promise2的then()方法,传入两个函数作为参数。第一个函数是成功回调函数,当promise2成功完成后,Promise对象就会调用这个函数,并将操作结果作为参数传递给该函数。第二个函数是失败回调函数,如果promise2失败,Promise对象就会调用这个函数,并将错误信息作为参数传递给该函数。

最后,我们调用setTimeout()方法来模拟两个异步操作。2秒后,我们调用resolve()方法来表示promise1成功,并将'Hello, Promise!'作为操作结果传递给该方法。1秒后,我们调用resolve()方法来表示promise2成功,并将'I am Promise!'作为操作结果传递给该方法。

Promise的错误处理

在使用Promise的过程中,我们可能会遇到异步操作失败的情况。为了处理这种情况,我们需要在then()方法中传入一个失败回调函数。当异步操作失败时,Promise对象就会调用这个函数,并将错误信息作为参数传递给该函数。

以下是一个使用Promise错误处理的示例:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('Oops, something went wrong!'));
  }, 2000);
});

promise.then(null, (error) => {
  console.log(error.message); // 输出: Oops, something went wrong!
});

在这个示例中,我们首先创建了一个Promise对象,并在构造函数中传入了一个函数作为参数。这个函数有两个参数,分别是resolve和reject。resolve表示异步操作成功,reject表示异步操作失败。

然后,我们调用Promise对象的then()方法,传入两个函数作为参数。第一个函数是成功回调函数,当异步操作成功完成后,Promise对象就会调用这个函数,并将操作结果作为参数传递给该函数。第二个函数是失败回调函数,如果异步操作失败,Promise对象就会调用这个函数,并将错误信息作为参数传递给该函数。

在失败回调函数中,我们打印错误信息。

手写Promise

现在我们已经了解了Promise的基本用法,那么我们就可以尝试自己手写一个简易的Promise了。

首先,我们需要定义一个Promise构造函数,这个构造函数有两个参数,分别是resolve和reject。resolve表示异步操作成功,reject表示异步操作失败。

function Promise(executor) {
  this.state = 'pending';
  this.result = undefined;
  this.onFulfilledCallbacks = [];
  this.onRejectedCallbacks = [];

  const resolve = (result) => {
    if (this.state !== 'pending') return;

    this.state = 'fulfilled';
    this.result = result;
    this.onFulfilledCallbacks.forEach((callback) => callback(result));
  };

  const reject = (error) => {
    if (this.state !== 'pending') return;

    this.state = 'rejected';
    this.result = error;
    this.onRejectedCallbacks.forEach((callback) => callback(error));
  };

  executor(resolve, reject);
}

然后,我们需要定义then()方法。then()方法有两个参数,分别是成功回调函数和失败回调函数。

Promise.prototype.then = function(onFulfilled, onRejected) {
  return new Promise((resolve, reject) => {
    if (this.state === 'fulfilled') {
      setTimeout(() => {
        try {
          const result = onFulfilled(this.result);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      }, 0);
    } else if (this.state === 'rejected') {
      setTimeout(() => {
        try {
          const result = onRejected(this.result);
          resolve(result);
        } catch (error) {
          reject(error);
        }
      }, 0);
    } else {
      this.onFulfilledCallbacks.push(() => {
        setTimeout(() => {
          try {
            const result = onFulfilled(this.result);
            resolve(result);
          } catch (error) {
            reject(error);
          }
        }, 0);
      });
      this.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            const result = onRejected(this.result);
            resolve(result);
          } catch (error) {
            reject(error);
          }
        }, 0);
      });
    }
  });
};

最后,我们需要定义catch()方法。catch()方法只有一个参数,即失败回调函数。

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

现在,我们就可以使用我们自己手写的Promise了。以下是一个使用我们自己手