Promise

95 阅读4分钟

在 JavaScript 中,我们经常会遇到异步操作,比如网络请求、文件读写等。这些操作不会立即返回结果,而是需要一定的时间,期间程序会继续执行其他任务。为了处理这种情况,我们通常使用回调函数来处理异步操作的结果。但是,当嵌套层数过多时,回调函数的代码会变得非常难以阅读和维护。这时,Promise 就成为了一个很好的解决方案。

Promise 是什么?

Promise 是 ECMAScript 6(ES6)中新增的一个特性,它可以更优雅地处理异步操作。Promise 表示一个异步操作最终将要产生的结果。Promise 对象有三种状态:

  • pending:初始状态,既不成功也不失败。
  • fulfilled:意味着操作成功完成,promise 的值将被传递给 then() 方法。
  • rejected:意味着操作失败,拒绝原因会被传递给 catch() 方法。

Promise 的基本用法

创建一个 Promise 对象需要提供一个函数作为参数,这个函数接受两个参数 resolvereject,分别表示操作成功和失败的回调函数。当我们调用 resolve() 函数时,Promise 对象的状态会被标记为 fulfilled,并把传入的参数作为成功的值;当我们调用 reject() 函数时,Promise 对象的状态会被标记为 rejected,并把传入的参数作为失败的原因。

javascript复制代码
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        let result = Math.random();
        if (result >= 0.5) {
            resolve(result);
        } else {
            reject('出错了');
        }
    }, 1000);
});

接下来我们可以使用 then() 方法和 catch() 方法来处理异步操作的结果。then() 方法接受两个参数:成功的回调函数和失败的回调函数。如果异步操作成功完成,则会执行第一个回调函数;如果异步操作失败,则会执行第二个回调函数。

javascript复制代码
promise.then(
    value => console.log(`成功:${value}`),
    reason => console.log(`失败:${reason}`)
);

catch() 方法只有一个参数,用于捕获异步操作的错误信息。如果在 then() 方法中的成功回调函数或失败回调函数中抛出了异常,都可以在 catch() 方法中捕获到。注意,catch() 方法只能捕获之前的异步操作的错误信息,不能捕获后续的错误信息。

javascript复制代码
promise.catch(reason => console.log(`出错了:${reason}`));

Promise 链式调用

Promise 还支持链式调用。这就意味着我们可以在一个 then() 方法中返回一个新的 Promise 对象,然后在这个新的 Promise 对象上继续调用 then() 方法。由于每个 then() 方法都会返回一个新的 Promise 对象,所以可以形成一个 Promise 链。

javascript复制代码
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        let result = Math.random();
        if (result >= 0.5) {
            resolve(result);
        } else {
            reject('出错了');
        }
    }, 1000);
});

promise.then(
    value => {
        console.log(`成功:${value}`);
        return value * 2;
    }
).then(
    value => console.log(`结果:${value}`)
).catch(reason => console.log(`出错了:${reason}`));

在上面的例子中,第一个 then() 方法返回了一个新的 Promise 对象,并将结果乘以 2。新的 Promise 对象又被传递给了第二个 then() 方法。最后,整个 Promise 链的执行结果都被捕获到了 catch() 方法中。

Promise.all 和 Promise.race

除了基本用法之外,Promise 还提供了两个常用的方法:Promise.all()Promise.race()

Promise.all() 方法接受一个数组作为参数,返回一个新的 Promise 对象。这个新的 Promise 对象会在数组中所有的 Promise 对象都成功完成时被标记为 fulfilled 状态,并把每个 Promise 对象的结果按照数组顺序组成一个新的数组传递给 then() 方法;如果有任何一个 Promise 对象被标记为 rejected 状态,则整个 Promise 链立即被标记为 rejected 状态,并把第一个被标记为 rejected 状态的 Promise 对象的拒绝原因传递给 catch() 方法。

javascript复制代码
let promise1 = new Promise(resolve => {
    setTimeout(() => {
        resolve('结果 1');
    }, 1000);
});

let promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('出错了');
    }, 500);
});

Promise.all([promise1, promise2]).then(
    values => console.log(`成功:${values}`),
    reason => console.log(`失败:${reason}`)
);

在上面的例子中,由于 promise2 被标记为 rejected 状态,所以整个 Promise 链立即被标记为 rejected 状态,并把 promise2 的拒绝原因传递给了 catch() 方法。

Promise.race() 方法也接受一个数组作为参数,返回一个新的 Promise 对象。这个新的 Promise 对象会在数组中任何一个 Promise 对象成功完成或失败时立即被标记为相应的状态,并把第一个成功完成或失败的 Promise 对象的结果或拒绝原因传递给 then() 方法或 catch() 方法。

javascript复制代码
let promise1 = new Promise(resolve => {
    setTimeout(() => {
        resolve('结果 1');
    }, 1000);
});

let promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('出错了');
    }, 500);
});

Promise.race([promise1, promise2]).then(
    value => console.log(`成功:${value}`),
    reason => console.log(`失败:${reason}`)
);

在上面的例子中,由于 promise2 先被标记为 rejected 状态,所以新的 Promise 对象也立即被标记为 rejected 状态,并把 promise2 的拒绝原因传递给了 catch() 方法。

总结

Promise 是一种更优雅地处理异步操作的方式。通过创建 Promise 对象、使用 then() 方法和 catch() 方法来处理异步操作的结果,我们可以避免回调函数嵌套过深的问题,提高代码的可读性和可维护性。除此之外,Promise 还提供了 Promise.all()Promise.race() 方法,让我们能够更方便地处理多个异步操作的结果。