当前位置:  首页>> 技术小册>> TypeScript 全面进阶指南

第二十四章:Promise与async/await

在TypeScript及现代JavaScript的开发中,异步编程是不可或缺的一部分。随着Web应用的日益复杂,处理网络请求、文件I/O、数据库操作等异步任务变得尤为重要。Promiseasync/await是JavaScript ES6及后续版本中引入的两种强大的异步编程模式,它们极大地简化了异步代码的编写与维护。本章将深入探讨这两种机制,并展示如何在TypeScript项目中高效地使用它们。

24.1 理解Promise

24.1.1 Promise的基本概念

Promise是JavaScript中用于异步计算的对象。一个Promise代表了一个最终可能完成(fulfilled)或失败(rejected)的异步操作及其结果值。Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),且状态一旦改变就不会再变。

24.1.2 Promise的创建

你可以通过new Promise(executor)构造函数来创建一个Promise对象,其中executor是一个执行器函数,它接受两个参数:resolvereject,分别用于处理异步操作成功和失败的情况。

  1. let promise = new Promise<string>((resolve, reject) => {
  2. setTimeout(() => {
  3. // 假设这是一个异步操作
  4. resolve("操作成功完成");
  5. // 或
  6. // reject(new Error("操作失败"));
  7. }, 1000);
  8. });

24.1.3 Promise的链式调用

Promise对象提供了.then().catch()方法用于处理异步操作的结果。.then()方法接受两个可选参数:一个用于处理fulfilled状态的函数,一个用于处理rejected状态的函数(或仅处理fulfilled状态的函数)。.catch()方法用于捕获.then()链中发生的错误。

  1. promise.then(result => {
  2. console.log(result); // "操作成功完成"
  3. }).catch(error => {
  4. console.error(error);
  5. });

24.1.4 Promise的静态方法

  • Promise.all(iterable):接收一个Promise对象的迭代器,并返回一个新的Promise实例,该实例在迭代器中的所有Promise都成功完成时才会完成。
  • Promise.race(iterable):与Promise.all类似,但Promise.race会在迭代器中的任何一个Promise完成时立即完成。
  • Promise.resolve(value):返回一个以给定值解析后的Promise对象。
  • Promise.reject(reason):返回一个以给定原因拒绝的Promise对象。

24.2 async/await 简介

24.2.1 async函数

async函数是声明为异步的函数,它通过返回一个Promise来隐式地处理异步操作。async函数内部可以使用await表达式来暂停函数的执行,等待Promise解决,然后继续执行async函数并返回解决结果。

  1. async function fetchData(): Promise<string> {
  2. let response = await fetch('https://api.example.com/data');
  3. let data = await response.json();
  4. return data.message;
  5. }

24.2.2 await表达式

await关键字只能在async函数内部使用。它用于等待一个Promise完成,并返回其解决的值。如果Promise被拒绝,则await表达式会抛出被拒绝的原因。

  1. async function run() {
  2. try {
  3. let message = await fetchData();
  4. console.log(message);
  5. } catch (error) {
  6. console.error(error);
  7. }
  8. }

24.2.3 错误处理

async函数中,你可以像处理普通函数中的错误一样,使用try...catch语句来捕获并处理由await表达式抛出的错误。

24.3 Promise与async/await的比较与选择

24.3.1 可读性

async/await使异步代码看起来和同步代码几乎一样,从而提高了代码的可读性和可维护性。相比之下,基于Promise的链式调用虽然功能强大,但复杂的逻辑可能会导致“回调地狱”(Callback Hell),降低代码的可读性。

24.3.2 错误处理

async/await中,错误处理更加直观,通过try...catch结构即可捕获并处理错误。而基于Promise的错误处理则需要通过.catch()方法或在.then()的第二个参数中处理,这在多层嵌套的Promise链中可能变得复杂。

24.3.3 适用性

  • 选择async/await:当你需要编写易于理解和维护的异步代码时,尤其是当异步逻辑较为复杂或需要多层嵌套时。
  • 选择Promise:在某些特定场景下,如需要并行处理多个异步任务并等待所有任务完成时,使用Promise.all等静态方法可能更为方便。

24.4 实战案例

案例一:使用async/await处理多个异步请求

假设我们需要从两个不同的API获取数据,并在两个请求都完成后进行处理。

  1. async function fetchMultipleData() {
  2. try {
  3. let response1 = await fetch('https://api.example1.com/data');
  4. let data1 = await response1.json();
  5. let response2 = await fetch('https://api.example2.com/data');
  6. let data2 = await response2.json();
  7. // 处理两个API返回的数据
  8. console.log(data1, data2);
  9. } catch (error) {
  10. console.error(error);
  11. }
  12. }

注意:在这个例子中,我们实际上是顺序地等待每个请求完成,而不是并行处理。要实现并行处理,可以考虑使用Promise.all结合async/await

案例二:使用Promise.all并行处理多个异步请求

  1. async function fetchMultipleDataInParallel() {
  2. try {
  3. let [response1, response2] = await Promise.all([
  4. fetch('https://api.example1.com/data'),
  5. fetch('https://api.example2.com/data')
  6. ]);
  7. let [data1, data2] = await Promise.all([
  8. response1.json(),
  9. response2.json()
  10. ]);
  11. // 处理两个API返回的数据
  12. console.log(data1, data2);
  13. } catch (error) {
  14. console.error(error);
  15. }
  16. }

24.5 总结

Promiseasync/await是现代JavaScript(包括TypeScript)中处理异步操作的两大支柱。Promise提供了一种优雅的方式来处理异步操作的成功和失败,而async/await则进一步简化了异步代码的编写,使其看起来和同步代码一样直观。通过掌握这两种机制,你可以编写出既高效又易于维护的异步代码。在实际开发中,应根据具体场景和需求灵活选择使用它们。


该分类下的相关小册推荐: