异步编程:async/await
本教程共 80 篇 · 第 74 篇 · 更新于 2026-08-10 · 约 13 分钟阅读
本节目标:搞懂 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";
}
}
ImportantTS 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>,T由return的值类型决定await自动解包 Promise,得到内部的类型await非 Promise 值不报错,类型保持不变- TS 7.0 strict 默认
useUnknownInCatchVariables,catch(err)中err为unknown - 操作
catch的错误前必须用instanceof或类型守卫收窄 for await..of遍历异步可迭代对象,循环变量类型自动从AsyncIterable<T>推断AsyncGenerator<T, TReturn, TNext>和Generator类似,只是next()返回 Promise