🆚 与 Java 的对比:Java 用线程池处理并发,TS/Node.js 用单线程事件循环。Java 的 CompletableFuture 类似 Promise,Java 21 的虚拟线程更接近 async/await 的体验。
🆚 与 Go 的对比:Go 用 goroutine + channel,TS 用 async/await + Promise。Go 的 error 返回值模式可以在 TS 中用 Result 类型模拟。
1. 为什么异步?
Node.js 采用单线程 + 事件循环模型。与 Java/C++ 的多线程不同,Node.js 不为每个请求创建线程,而是通过非阻塞 IO 和回调来处理并发:
❌ 多线程模型的挑战
- • 线程创建和上下文切换开销大
- • 共享内存需要锁,易死锁
- • 调试困难,竞态条件难复现
✅ 事件循环的优势
- • 单线程无锁,代码更简单
- • 非阻塞 IO,高并发低开销
- • 特别适合 IO 密集型应用
对 CPU 密集型任务,Node.js 可通过 Worker Threads 利用多核;但异步模型本身主要解决 IO 等待问题。
2. 回调函数
回调是最原始的异步模式——将一个函数作为参数传递,在异步操作完成时调用:
import { readFile } from "fs";
// Node.js 风格回调:第一个参数是 error
readFile("config.json", "utf-8", (err, data) => {
if (err) {
console.error("读取失败:", err.message);
return;
}
console.log("文件内容:", data);
});
// 回调地狱(Callback Hell)——嵌套回调导致代码不可维护
readFile("a.txt", "utf-8", (err, a) => {
if (err) return;
readFile("b.txt", "utf-8", (err, b) => {
if (err) return;
readFile("c.txt", "utf-8", (err, c) => {
if (err) return;
console.log(a + b + c); // 三层嵌套,持续恶化...
});
});
});
3. Promise
Promise 表示一个异步操作的最终结果,有三种状态:pending(进行中)、fulfilled(成功)、rejected(失败)。
// 创建 Promise
function readFileAsync(path: string): Promise<string> {
return new Promise((resolve, reject) => {
readFile(path, "utf-8", (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
}
// 链式调用——避免回调嵌套
readFileAsync("config.json")
.then(data => JSON.parse(data))
.then(config => {
console.log("端口:", config.port);
return config.port;
})
.catch(err => console.error("失败:", err.message))
.finally(() => console.log("操作完成"));
// Promise 的类型标注
const numPromise: Promise<number> = Promise.resolve(42);
const strPromise: Promise<string> = new Promise(resolve => {
setTimeout(() => resolve("done"), 1000);
});
4. async / await
async/await 是 Promise 的语法糖,让异步代码看起来像同步代码:
// async 函数始终返回 Promise
async function loadConfig(): Promise<Record<string, unknown>> {
const data = await readFileAsync("config.json");
return JSON.parse(data);
}
// 用 try/catch 处理错误
async function main(): Promise<void> {
try {
const config = await loadConfig();
console.log("配置:", config);
} catch (err) {
console.error("加载失败:", err);
}
}
// 从回调 → Promise → async/await 的演进
// 回调: readFile(path, cb)
// Promise: readFileAsync(path).then(data => ...)
// async: const data = await readFileAsync(path)
// 并行执行多个 await
async function loadAll(): Promise<void> {
// ✗ 串行——慢
const a = await fetchUser();
const b = await fetchOrders();
// ✓ 并行——快
const [user, orders] = await Promise.all([
fetchUser(),
fetchOrders()
]);
}
5. Promise 组合器
const tasks = [fetchA(), fetchB(), fetchC()];
// Promise.all —— 全部成功才成功,一个失败就失败
const [a, b, c] = await Promise.all(tasks);
// 类型自动推断为各 Promise 的结果类型元组
// Promise.allSettled —— 等待全部完成,不管成功失败
const results = await Promise.allSettled(tasks);
results.forEach(r => {
if (r.status === "fulfilled") console.log("成功:", r.value);
else console.log("失败:", r.reason);
});
// Promise.race —— 返回最先完成的(无论成功失败)
const fastest = await Promise.race([
fetchFromCDN(),
fetchFromOrigin()
]);
// Promise.any —— 返回最先成功的,全部失败才失败
const firstSuccess = await Promise.any([
fetchFromCDN(),
fetchFromOrigin()
]);
选择指南:all 适合全部必须成功;allSettled 适合容忍部分失败;race 适合超时控制;any 适合多源冗余。
6. 事件循环
理解事件循环是掌握 Node.js/浏览器异步行为的关键:
console.log("1: 同步");
setTimeout(() => console.log("2: 宏任务 (setTimeout)"), 0);
Promise.resolve().then(() => console.log("3: 微任务 (Promise)"));
console.log("4: 同步");
// 输出顺序: 1 → 4 → 3 → 2
执行顺序规则:
- 同步代码——立即执行,进入调用栈
- 微任务(Promise.then、queueMicrotask)——当前同步代码执行完毕后立即执行
- 宏任务(setTimeout、setInterval、IO 回调)——在所有微任务执行完后,从宏任务队列取一个执行
- 每个宏任务执行后,再次清空微任务队列,循环往复
7. 错误处理模式
// 自定义错误类
class AppError extends Error {
constructor(
message: string,
public code: string,
public statusCode: number = 500
) {
super(message);
this.name = "AppError";
}
}
// catch 中 error 类型默认是 unknown,需要缩窄
async function fetchData(url: string): Promise<unknown> {
try {
const res = await fetch(url);
if (!res.ok) throw new AppError("请求失败", "FETCH_ERROR", res.status);
return await res.json();
} catch (err: unknown) {
if (err instanceof AppError) {
console.error(`[${err.code}] ${err.message}`);
} else if (err instanceof Error) {
console.error("未知错误:", err.message);
} else {
console.error("非标准错误:", err);
}
throw err;
}
}
// Result 模式——类似 Go/Rust 的错误处理(无需 try/catch)
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
async function safeFetch(url: string): Promise<Result<unknown>> {
try {
const res = await fetch(url);
const data = await res.json();
return { ok: true, value: data };
} catch (err) {
return { ok: false, error: err instanceof Error ? err : new Error(String(err)) };
}
}
// 使用 Result
const result = await safeFetch("/api/users");
if (result.ok) {
console.log("数据:", result.value);
} else {
console.error("错误:", result.error.message);
}
📝 本章要点
- ✦Node.js 单线程事件循环模型通过非阻塞 IO 实现高并发,不同于 Java 的多线程模型
- ✦异步发展路径:回调 → Promise → async/await,现代代码应始终使用 async/await
- ✦独立的异步操作用
Promise.all并行执行,而非依次 await - ✦微任务优先于宏任务执行——这决定了 Promise 回调和 setTimeout 的执行顺序
- ✦
catch中的err是unknown类型,必须用instanceof缩窄后使用。Result 模式是 try/catch 的类型安全替代方案