首页 / TypeScript 入门教程 / 异步编程:async/await

TypeScript 入门教程

异步编程:async/await

本教程共 80 篇 · 第 74 篇 · 更新于 2026-08-10 · 约 13 分钟阅读

TypeScriptTypeScript 入门教程asyncawait异步编程for await of

本节目标:搞懂 TypeScript 怎么处理 async/await——async 函数的返回类型规则、await 解包 Promise 时类型如何变化、try/catch 在 async 里的类型处理、还有 for await..of 的类型推断。

async 函数的返回类型

async 函数最核心的规则:返回值自动包装成 Promise。TypeScript 会根据你 return 的值自动推断 T

async function fetchName(): Promise<string> {
  return "Alice";
  // TS 知道返回类型是 Promise<string>
}

即使你不写 Promise<string> 标注,TS 也能推断出来:

async function fetchAge() {
  return 25;
}
// 推断返回类型:Promise<number>

别把 T 写成返回值本身的类型

新手常见的错误:

// ❌ 错误:把返回类型写成 string
async function getName(): string {
  return "Alice";
}
// TS 报错:异步函数的返回类型必须是 Promise 类型

规则很简单:async 函数的返回类型一定是 Promise<T>T 是你 return 的那个值的类型。

返回 void 和 never

async function doSideEffect(): Promise<void> {
  console.log("执行副作用");
  // 没有 return,等价于 return undefined
}

async function throwAlways(): Promise<never> {
  throw new Error("永远不会成功");
}

Promise<void> 表示”完成了但没有有意义的值”。Promise<never> 表示”这玩意永远到不了 resolve 那一步”。

await 的类型解包

await 会从 Promise 里”拆出”内部的类型:

async function getValue() {
  const num = await Promise.resolve(42);
  // num 类型:number(不是 Promise<number>)

  const str = await Promise.resolve("hello");
  // str 类型:string

  return num + str.length;
  // 返回类型:Promise<number>
}

await 非 Promise 值

await 后面跟的不是 Promise 也没关系,TS 不会报错:

async function demo() {
  const x = await 42;
  // x 类型:number —— await 一个非 Promise 值,原样返回
}

JS 规范里 await 会把非 Promise 值包进一个已经 resolved 的 Promise,所以类型上 await 42 就是 number

多层 await

async function unwrap() {
  const p = Promise.resolve(Promise.resolve(42));
  // p 类型:Promise<Promise<number>>

  const n = await p;
  // n 类型:number(自动递归解包)
}

Promise 会自动递归展平,await 后面 Promise<Promise<number>> 得到的是 number。TS 的类型系统正确反映了这一点。

try/catch 在 async 中的类型

async 函数 + try/catch 是处理异步错误的经典模式:

async function fetchData(url: string): Promise<string> {
  try {
    const response = await fetch(url);
    const data = await response.text();
    return data;
  } catch (err) {
    // err 类型:unknown(TS 7.0 strict 默认)
    console.error("请求失败:", err);
    return "fallback";
  }
}
Important

TS 7.0 的 strict 模式下,useUnknownInCatchVariables 默认为 true,所以 catch (err)err 类型是 unknown,不是 any。这意味着你操作 err 之前必须做类型收窄。

为什么 catch 里是 unknown

因为 throw 可以抛出任何东西——字符串、数字、undefined、Error 实例都行:

try {
  throw "哈哈"; // 抛一个字符串
} catch (err) {
  // err 是 unknown,因为不知道运行时到底抛了啥
}

最佳实践是用 instanceof 收窄:

async function safeFetch(url: string): Promise<string | null> {
  try {
    const response = await fetch(url);
    return await response.text();
  } catch (err) {
    if (err instanceof Error) {
      console.error(err.message);
    } else {
      console.error("未知错误:", String(err));
    }
    return null;
  }
}

async 函数中的 return 和 throw

async function divide(a: number, b: number): Promise<number> {
  if (b === 0) {
    throw new Error("除数不能为 0"); // 变成了 rejected Promise
  }
  return a / b; // 变成了 resolved Promise
}
  • return 的值变成 Promise.resolve(value)
  • throw 变成 Promise.reject(error)

调用方用 try/catch 捕获:

async function main() {
  try {
    const result = await divide(10, 0);
    console.log(result);
  } catch (err) {
    console.error(err); // "除数不能为 0"
  }
}

async 匿名函数和箭头函数

类型标注方式都类似:

// 箭头函数
const fetchUser = async (id: number): Promise<User> => {
  const res = await fetch(`/api/user/${id}`);
  return res.json();
};

// 匿名函数作为回调
[1, 2, 3].forEach(async (id) => {
  const user = await fetchUser(id);
  console.log(user);
});
// 注意:forEach 的回调是 async 时,forEach 本身不会等待
Warning

array.forEach(async () => {...}) 不会等待异步回调完成。如果你想按顺序执行异步操作,用 for..of + await 而不是 forEach

异步迭代:for await..of

for await..of 专门用来遍历异步可迭代对象(实现了 Symbol.asyncIterator 的对象):

async function* generateSequence() {
  let i = 0;
  while (i < 3) {
    await new Promise((r) => setTimeout(r, 500));
    yield i++;
  }
}

async function consume() {
  for await (const value of generateSequence()) {
    // value 类型:number
    console.log(value);
  }
}

for await..of 的循环变量类型,来自异步迭代器的泛型参数。上面 generateSequence 返回 AsyncGenerator<number, void, unknown>,所以 value 推断为 number

遍历异步流数据

for await..of 最实用的场景是读取流式数据,比如 Node.js 的 ReadableStream

async function processStream(stream: ReadableStream<Uint8Array>) {
  for await (const chunk of stream) {
    // chunk 类型:Uint8Array
    console.log(`收到 ${chunk.length} 字节`);
  }
}

TypeScript 能从 ReadableStream<T> 的泛型中推断出 chunk 的类型。

ReadableStream 的泛型异步迭代器类型

TS 7.0 内置了完整的异步迭代器类型层次:

// 标准库中的类型(简化版)
interface AsyncIterator<T> {
  next(): Promise<IteratorResult<T>>;
}

interface AsyncIterable<T> {
  [Symbol.asyncIterator](): AsyncIterator<T>;
}

interface AsyncGenerator<T = unknown, TReturn = any, TNext = unknown>
  extends AsyncIterator<T> {
  next(value?: TNext): Promise<IteratorResult<T>>;
  return(value: TReturn): Promise<IteratorResult<T>>;
  throw(e: any): Promise<IteratorResult<T>>;
  [Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;
}

和同步版本的 Generator 结构类似,区别只是 next() / return() / throw() 都返回 Promise<IteratorResult<T>>

小结

  • async 函数的返回类型一定是 Promise<T>Treturn 的值类型决定
  • await 自动解包 Promise,得到内部的类型
  • await 非 Promise 值不报错,类型保持不变
  • TS 7.0 strict 默认 useUnknownInCatchVariablescatch(err)errunknown
  • 操作 catch 的错误前必须用 instanceof 或类型守卫收窄
  • for await..of 遍历异步可迭代对象,循环变量类型自动从 AsyncIterable<T> 推断
  • AsyncGenerator<T, TReturn, TNext>Generator 类似,只是 next() 返回 Promise