promise详解

866 阅读7分钟

Promise 是 JavaScript 中一种用于异步编程的解决方案,最早由社区成员发起并标准化于 ES6 中。它解决了回调地狱、代码可读性和可维护性等问题,同时提供了很多强大的功能和应用场景。本文将详细介绍 Promise 的基本概念、使用方法和实现原理,并结合实例进行说明。

Promise 的基本概念

Promise 是一种表示异步操作的对象,可以在异步操作完成后进行状态的改变和相应操作的执行。Promise 对象有三个状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。当 Promise 对象的状态从 pending 转换为 fulfilled 或 rejected 时,称为 Promise 对象“resolved”(解决),状态不可逆转。同时,Promise 对象还有一个 value 属性,用于存储 Promise 对象的返回值或错误信息。

创建 Promise 对象的方式如下:

const promise = new Promise((resolve, reject) => {
  // 异步操作,例如向服务器请求数据等
  if (异步操作成功) {
    resolve(返回值);
  } else {
    reject(错误信息);
  }
});

其中,Promise 构造函数接受一个函数作为参数,这个函数包含两个参数:resolve 和 reject,分别表示异步操作成功和失败时的回调函数。当异步操作成功时,调用 resolve 方法并传入返回值,会使 Promise 对象的状态从 pending 变为 fulfilled,此时 then 方法中的成功回调函数会被执行。当异步操作失败时,调用 reject 方法并传入错误信息,会使 Promise 对象的状态从 pending 变为 rejected,此时 then 方法中的失败回调函数会被执行。

Promise 的使用方法

Promise 提供了一些方法来处理异步操作结果:then()、catch() 和 finally()。

then()

then() 方法接受两个参数:成功回调函数和失败回调函数。

promise.then(
  function(value) {
    // 当异步操作成功时执行
  },
  function(error) {
    // 当异步操作失败时执行
  }
);

当 Promise 对象的状态变为 fulfilled 时,then() 方法中的成功回调函数会被调用并传入返回值。当 Promise 对象的状态变为 rejected 时,then() 方法中的失败回调函数会被调用并传入错误信息。

catch()

catch() 方法用于捕获异常并处理,相当于在 then() 方法中只指定失败回调函数的简写方式。

promise.catch(function(error) {
  // 异步操作失败时执行
});

如果 Promise 对象状态变为 rejected 时,catch() 方法中的回调函数会被调用并传入错误信息。如果 Promise 对象在 then() 方法中未指定失败回调函数,则会进一步抛出异常并被 catch() 方法捕获。

finally()

finally() 方法用于在 Promise 对象无论是 resolved 还是 rejected 后都执行一段代码。

promise.finally(function() {
  // 无论异步操作是成功还是失败都会执行
});

当 Promise 对象状态变为 resolved 或 rejected 时,finally() 方法中的回调函数都会被调用。该方法不接受任何参数。

Promise 的实现原理

Promise 的实现原理是基于事件循环机制和异步任务队列。在 JavaScript 中,执行顺序遵循事件循环机制,即先进入主线程执行同步任务,然后再添加异步任务到异步任务队列中。异步任务包括定时器、事件处理函数和 Ajax 等,它们需要等待一定的时间或事件才能继续执行。

Promise 的实现原理就是借助了异步任务队列。当 Promise 对象的状态变为 fulfilled 或 rejected 时,会依次将所有通过 then() 方法注册的回调函数添加到异步任务队列中,等待 JavaScript 主线程执行完同步任务后再按顺序执行异步回调函数。

同时,Promise 还提供了一些其他的静态方法和实例方法,例如 Promise.all()、Promise.race()、Promise.resolve() 和 Promise.reject() 等,用于处理多个 Promise 对象或快速创建 Promise 对象等。需要根据具体需求选择合适的方法来处理异步任务。

实例说明

下面是一个使用 Promise 对象获取 GitHub API 数据的实例代码:

const getApiResponse = () => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', 'https://api.github.com/users/octocat');
    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(xhr.response);
      } else {
        reject(xhr.statusText);
      }
    };
    xhr.onerror = () => {
      reject('请求出错');
    };
    xhr.send();
  });
};

getApiResponse()
  .then((data) => {
    console.log(JSON.parse(data));
  })
  .catch((error) => {
    console.error('请求失败', error);
  })
  .finally(() => {
    console.log('请求结束');
  });

该代码使用 XMLHttpRequest 对象向 GitHub API 发送异步请求,并将返回结果封装为 Promise 对象。当请求成功时,调用 resolve 方法并传入返回值,当请求失败时,调用 reject 方法并传入错误信息。通过 then()、catch() 和 finally() 方法处理异步操作结果,并输出相应信息。

Promise.all()示例

以下是一个使用 Promise.all() 方法的示例代码,其中包括了两个异步操作,只有当两个操作均成功时才会执行 Promise.all() 的成功回调函数,并返回结果数组。

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

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Promise 2 resolved');
  }, 3000);
});

Promise.all([promise1, promise2])
  .then(result => {
    console.log(result); // ['Promise 1 resolved', 'Promise 2 resolved']
  })
  .catch(error => {
    console.error(error); // 如果有一个异步操作出错则会进入 catch 分支
  });

当两个异步操作均成功后,Promise.all() 的成功回调函数会被调用,并返回一个数组,数组中包含了每个 Promise 对象的返回值。如果两个异步操作中有一个失败,则 Promise.all() 的失败回调函数会被调用。

Promise.race()示例

以下是一个使用 Promise.race() 方法的示例代码,我们创建了两个 Promise 对象,第一个 Promise 会在 2 秒钟后成功,第二个 Promise 会在 3 秒钟后失败。通过 Promise.race() 方法将这两个 Promise 对象作为参数传递进去,返回一个新的 Promise 对象。

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

const promise2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('Promise 2 rejected');
  }, 3000);
});

Promise.race([promise1, promise2])
  .then(result => {
    console.log(result); // 'Promise 1 resolved'
  })
  .catch(error => {
    console.error(error); // 'Promise 2 rejected'
  });

在上面的代码中,我们首先创建了两个 Promise 对象,分别代表两个异步操作。接着,通过 Promise.race() 方法将这两个 Promise 对象作为参数传递进去,返回一个新的 Promise 对象。在这个例子中,因为第一个 Promise 会先完成,所以 Promise.race() 的成功回调函数会被调用,并返回结果。

如果两个异步操作中有一个成功或失败,则 Promise.race() 的成功或失败回调函数会被调用,这取决于哪一个 Promise 先完成或失败。

Promise.all()与Promise.race()区别

Promise.all() 会等待所有的异步操作都完成(即所有的 Promise 对象都进入 resolved 状态),然后才会执行其后的 then() 方法,返回一个包含所有异步操作返回值的数组。如果其中有任何一个异步操作失败,则整个 Promise 对象会被标记为 rejected 状态,且失败回调函数会被调用。因此,Promise.all() 更适合处理多个异步操作的并行执行,只有当所有异步操作都成功完成时才会继续任务执行。

Promise.race() 则是只要其中任何一个异步操作完成(即有一个 Promise 对象进入 resolved 或 rejected 状态),就会立即结束并返回对应的结果或错误。也就是说,Promise.race() 更适合处理多个异步操作的竞速执行,只有最先完成的异步操作的结果会影响后续的执行。

综上所述,Promise.all()Promise.race() 的区别在于处理多个异步操作的方式:Promise.all() 是等待所有异步操作都完成后再继续执行,而 Promise.race() 则是只要有一个 Promise 对象完成时就立即结束并返回结果/错误。

总结

Promise 是 JavaScript 中一种用于异步编程的解决方案,能够更加方便和优雅地处理异步任务和异常处理。它是基于事件循环机制和异步任务队列实现的,因此需要注意异步任务的执行顺序和时间。同时,还需要掌握 Promise 的基本概念、使用方法和实现原理,才能更好地运用它处理实际业务场景中的异步操作。