Skip to content

第9章 异步编程

本章定位:深入学习TypeScript的异步编程模型,包括Promise基础、async/await语法、错误处理、并发模式,以及与Python asyncio的对比。


Promise是JavaScript/TypeScript中处理异步操作的对象,代表一个异步操作的最终结果(成功或失败)。

// Promise的三个状态
// 1. Pending(待定)- 初始状态
// 2. Fulfilled(已兑现)- 操作成功
// 3. Rejected(已拒绝)- 操作失败
// 基本Promise
const promise = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve("Success!");
}, 1000);
});
promise.then(result => {
console.log(result); // "Success!"
});
// 拒绝的Promise
const failedPromise = new Promise<string>((resolve, reject) => {
setTimeout(() => {
reject(new Error("Failed!"));
}, 1000);
});
failedPromise.catch(error => {
console.error(error.message); // "Failed!"
});

Python对比:

# Python asyncio
import asyncio
async def async_operation():
await asyncio.sleep(1)
return "Success!"
async def main():
result = await async_operation()
print(result)
asyncio.run(main())
// state: pending -> fulfilled
const successPromise = new Promise((resolve) => {
resolve("done");
});
// state: pending -> rejected
const errorPromise = new Promise((_, reject) => {
reject(new Error("error"));
});
// 一旦Promise settled,状态不可变
const promise = new Promise((resolve) => {
resolve("first");
resolve("second"); // 被忽略
});
// .then() 返回新的Promise
fetchUser(1)
.then(user => fetchUserPosts(user.id))
.then(posts => filterPublicPosts(posts))
.then(publicPosts => publicPosts.length)
.then(count => console.log(`${count} public posts`))
.catch(error => console.error(error));
// Promise.all - 所有都成功才成功
const promises = [
fetchUser(1),
fetchUser(2),
fetchUser(3)
];
const users = await Promise.all(promises);
// Promise.allSettled - 所有完成(不管成功失败)
const results = await Promise.allSettled(promises);
results.forEach((result, i) => {
if (result.status === "fulfilled") {
console.log(`User ${i}: ${result.value}`);
} else {
console.log(`User ${i}: ${result.reason}`);
}
});
// Promise.race - 最先完成的(成功或失败)
const first = await Promise.race([
fetchFromServer1(),
fetchFromServer2(),
fetchFromServer3()
]);
// Promise.any - 最先成功的(忽略失败)
const any = await Promise.any([
fetchPrimary(),
fetchBackup()
]);

async/await是Promise的语法糖,让异步代码看起来像同步代码:

// async函数自动返回Promise
async function fetchUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
// 调用async函数
fetchUser(1).then(user => console.log(user));

Python对比:

import aiohttp
async def fetch_user(session: aiohttp.ClientSession, user_id: int) -> dict:
async with session.get(f"/api/users/{user_id}") as response:
return await response.json()
async def main():
user = await fetch_user(session, 1)
print(user)
async function process() {
try {
const result = await someAsyncOperation();
console.log(result);
} catch (error) {
console.error(error);
}
}
// 显式返回Promise
async function getNumber(): Promise<number> {
return 42; // 自动包装为Promise
}
// 隐式推断
async function getString() {
return "hello"; // 推断为 Promise<string>
}
// void async函数
async function logSomething() {
console.log("logging");
} // Promise<void>
// 顺序执行
async function processAll(items: string[]) {
for (const item of items) {
const result = await processItem(item);
console.log(result);
}
}
// 并行执行
async function processAllParallel(items: string[]) {
const promises = items.map(item => processItem(item));
const results = await Promise.all(promises);
console.log(results);
}
// 错误处理
async function processWithErrors(items: string[]) {
const results = await Promise.allSettled(
items.map(item => processItem(item))
);
results.forEach((result, i) => {
if (result.status === "fulfilled") {
console.log(`Item ${i}: ${result.value}`);
} else {
console.error(`Item ${i} failed: ${result.reason}`);
}
});
}

async function fetchData(url: string) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
if (error instanceof TypeError) {
console.error("Network error:", error);
} else if (error instanceof Error) {
console.error("Server error:", error.message);
}
throw error; // 重新抛出
}
}
async function main() {
try {
await riskyOperation();
} catch (error) {
console.error("Operation failed:", error);
// 错误可以被上层捕获
throw new Error("Wrapper", { cause: error });
}
}
// 调用者
try {
await main();
} catch (error) {
console.error("Failed:", error);
}
async function withCleanup() {
let connection: Connection;
try {
connection = await getConnection();
const result = await connection.query("SELECT * FROM users");
return result;
} catch (error) {
console.error("Query failed:", error);
throw error;
} finally {
// 无论成功还是失败,都会执行
if (connection) {
await connection.close();
}
}
}

Python对比:

async def with_cleanup():
connection = None
try:
connection = await get_connection()
result = await connection.query("SELECT * FROM users")
return result
except Exception as e:
print(f"Query failed: {e}")
raise
finally:
if connection:
await connection.close()
// 自定义错误类型
class AppError extends Error {
constructor(
message: string,
public code: string,
public status: number = 500
) {
super(message);
this.name = "AppError";
}
}
// 使用
async function fetchData(url: string) {
try {
const response = await fetch(url);
if (response.status === 404) {
throw new AppError("Not found", "NOT_FOUND", 404);
}
return response.json();
} catch (error) {
if (error instanceof AppError) {
console.error(`${error.code}: ${error.message}`);
}
throw error;
}
}

fetchUser(1)
.then(user => user.posts)
.then(posts => posts.filter(p => p.public))
.then(publicPosts => publicPosts.length)
.then(count => {
console.log(`Found ${count} public posts`);
})
.catch(error => {
console.error("Failed:", error);
})
.finally(() => {
console.log("Operation complete");
});
// 避免嵌套
doSomethingA()
.then(resultA => {
// 错误的嵌套方式
doSomethingB(resultA).then(resultB => {
doSomethingC(resultB).then(resultC => {
console.log(resultC);
});
});
});
// 正确的扁平方式
doSomethingA()
.then(resultA => doSomethingB(resultA))
.then(resultB => doSomethingC(resultB))
.then(resultC => console.log(resultC));
// Promise.resolve - 创建已成功的Promise
const resolved = Promise.resolve(42);
resolved.then(v => console.log(v)); // 42
// Promise.reject - 创建已失败的Promise
const rejected = Promise.reject(new Error("failed"));
// 转换同步值为Promise
async function getUser() {
const user = await Promise.resolve({ id: 1, name: "Alice" });
return user;
}
// 顺序执行
async function sequence(tasks: (() => Promise<void>)[]) {
for (const task of tasks) {
await task();
}
}
// 并行执行
async function parallel(tasks: (() => Promise<void>)[]) {
await Promise.all(tasks.map(task => task()));
}
// 限流
async function limitedParallel(tasks: (() => Promise<void>)[], limit: number) {
const chunks: (() => Promise<void>)[] = [];
for (let i = 0; i < tasks.length; i += limit) {
chunks.push(() => Promise.all(tasks.slice(i, i + limit)));
}
for (const chunk of chunks) {
await chunk();
}
}

async function sequential() {
const a = await stepA(); // 等待A完成
const b = await stepB(); // 等待B完成
const c = await stepC(); // 等待C完成
return [a, b, c];
}
async function parallel() {
const [a, b, c] = await Promise.all([
stepA(),
stepB(),
stepC()
]);
return { a, b, c };
}
// 多个请求竞争,只用最快的结果
async function race() {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 1000);
try {
const result = await Promise.race([
fetch("/api/fast", { signal: controller.signal }),
fetch("/api/reliable")
]);
return result.json();
} finally {
clearTimeout(timeoutId);
}
}
const cache = new Map<string, Promise<string>>();
async function fetchWithCache(url: string): Promise<string> {
if (cache.has(url)) {
return cache.get(url)!;
}
const promise = fetch(url).then(r => r.text());
cache.set(url, promise);
return promise;
}
async function parallelLimit(
tasks: (() => Promise<void>)[],
limit: number
): Promise<void> {
const executing: Promise<void>[] = [];
for (const task of tasks) {
const p = task().finally(() => {
executing.splice(executing.indexOf(p), 1);
});
executing.push(p);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
await Promise.all(executing);
}

Python对比:

import asyncio
async def limited_parallel(tasks: list, limit: int):
semaphore = asyncio.Semaphore(limit)
async def with_semaphore(task):
async with semaphore:
return await task()
await asyncio.gather(*[with_semaphore(t) for t in tasks])

概念TypeScript PromisePython asyncio
异步单元Promise对象coroutine函数
等待awaitawait
并发Promise.allasyncio.gather
错误传播try/catchtry/except
生成器无async def

TypeScript Promise:

// 创建Promise
const promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("done"), 1000);
});
// 链式
promise
.then(result => result.toUpperCase())
.then(upper => console.log(upper))
.catch(err => console.error(err));
// async/await
async function main() {
try {
const result = await promise;
console.log(result);
} catch (e) {
console.error(e);
}
}

Python asyncio:

import asyncio
# 创建coroutine
async def async_operation():
await asyncio.sleep(1)
return "done"
# 链式
async def main():
try:
result = await async_operation()
print(result.upper())
except Exception as e:
print(e)
asyncio.run(main())

TypeScript:

// 并行
await Promise.all([taskA(), taskB(), taskC()]);
// 顺序
for (const task of [taskA, taskB, taskC]) {
await task();
}

Python:

# 并行
await asyncio.gather(task_a(), task_b(), task_c())
# 顺序
for task in [task_a, task_b, task_c]:
await task()

TypeScript:

try {
await riskyOperation();
} catch (error) {
if (error instanceof SpecificError) {
handle(error);
}
throw error;
}

Python:

try:
await risky_operation()
except SpecificError as e:
handle(e)
raise

TypeScript:

const connection = await createConnection();
try {
const result = await connection.query();
return result;
} finally {
await connection.close();
}

Python:

connection = await create_connection()
try:
result = await connection.query()
return result
finally:
await connection.close()
# 或者使用async with
async with create_connection() as connection:
result = await connection.query()

async function* generateNumbers(): AsyncGenerator<number> {
for (let i = 0; i < 5; i++) {
await new Promise(resolve => setTimeout(resolve, 100));
yield i;
}
}
async function main() {
for await (const num of generateNumbers()) {
console.log(num);
}
}
class AsyncQueue<T> implements AsyncIterable<T> {
private queue: T[] = [];
private resolvers: ((value: T) => void)[] = [];
async *[Symbol.asyncIterator](): AsyncIterableIterator<T> {
while (true) {
if (this.queue.length > 0) {
const item = this.queue.shift()!;
yield item;
} else {
const item = await new Promise<T>(resolve => {
this.resolvers.push(resolve as any);
});
yield item;
}
}
}
push(item: T) {
if (this.resolvers.length > 0) {
const resolve = this.resolvers.shift()!;
resolve(item);
} else {
this.queue.push(item);
}
}
}

本章学习了TypeScript的异步编程:

  1. Promise基础:

    • 三个状态:pending、fulfilled、rejected
    • then/catch/finally链式调用
    • Promise.all/allSettled/race/any
  2. async/await:

    • async函数返回Promise
    • await等待Promise完成
    • 让异步代码更像同步
  3. 错误处理:

    • try/catch/finally
    • 错误传播
    • 自定义错误类型
  4. 并发模式:

    • 顺序执行
    • 并行执行
    • 限流和缓存
  5. Python对比:

    • Python用async def,TypeScript用async function
    • Promise.all对应asyncio.gather
    • 都支持async迭代器

创建一个延迟解析的Promise,并在1秒后打印结果。

将Promise链式调用改写成async/await形式。

并行获取3个用户数据,并打印。

实现一个带重试的异步函数。

用Python asyncio实现同样的功能,对比语法差异。



下一章我们将学习TypeScript的类型声明与声明文件,了解如何为JavaScript代码提供类型信息,以及 DefinitelyTyped 的使用。