显示页面讨论过去修订反向链接回到顶部 本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。 ====== TypeScript 中异步编程知识点详解 ====== TypeScript 中的异步编程,是前端与 Node.js 开发中极其重要的一部分。无论是发送网络请求、读取文件、执行定时任务,还是数据库访问,都离不开“异步”机制。下面我将使用 DokuWiki 语法,系统讲解 TypeScript 中与异步相关的核心知识点。 ===== 一、什么是异步 ===== 在理解 TypeScript 异步之前,首先要明白“同步”和“异步”的区别。 ==== 1. 同步 ==== 同步代码会按照从上到下的顺序依次执行,前面的任务没有完成,后面的任务就不能开始。 <code ts> console.log("任务1"); console.log("任务2"); console.log("任务3"); </code> 执行结果: <code> 任务1 任务2 任务3 </code> 这种执行方式简单直观,但如果某个任务耗时很长,例如网络请求或者文件读取,那么后续任务就会被阻塞。 ==== 2. 异步 ==== 异步代码的特点是:某个耗时任务启动后,不必等待它完成,程序可以继续执行后面的代码,等任务完成后再处理结果。 <code ts> console.log("开始"); setTimeout(() => { console.log("异步任务完成"); }, 1000); console.log("结束"); </code> 执行顺序通常为: <code> 开始 结束 异步任务完成 </code> 这说明 `setTimeout` 中的逻辑并不会阻塞后续代码执行。 ===== 二、为什么 TypeScript 需要异步 ===== TypeScript 是 JavaScript 的超集,因此它的异步模型本质上继承自 JavaScript。之所以需要异步,主要有以下原因: * 网络请求耗时不可控 * 文件读写通常不是瞬时完成 * 数据库查询需要等待返回结果 * 定时器和用户交互事件本身就具有延迟性 * 为了避免阻塞主线程,提高程序响应能力 例如,浏览器中如果所有请求都采用同步方式,那么页面会卡住,用户体验会非常差。 ===== 三、JavaScript 事件循环与异步基础 ===== 虽然题目是 TypeScript,但 TypeScript 的异步建立在 JavaScript 运行机制之上,所以必须了解事件循环(Event Loop)。 ==== 1. 调用栈 ==== 调用栈负责执行当前同步代码。函数调用时入栈,执行完成后出栈。 ==== 2. Web APIs / Node APIs ==== 像 `setTimeout`、HTTP 请求、文件系统操作等,不是直接由 JavaScript 引擎完成,而是交给宿主环境处理。 ==== 3. 回调队列 ==== 异步任务完成后,对应的回调函数会进入任务队列,等待调用栈空闲后执行。 ==== 4. 事件循环 ==== 事件循环不断检查调用栈是否为空,如果为空,就从任务队列中取出任务执行。 可以简单理解为: - 同步代码先执行 - 异步任务先注册 - 完成后的回调排队 - 栈空了再执行回调 ===== 四、回调函数(Callback) ===== 在 Promise 出现之前,JavaScript 主要通过回调函数处理异步逻辑。 ==== 1. 基本写法 ==== <code ts> function fetchData(callback: (data: string) => void): void { setTimeout(() => { callback("获取到的数据"); }, 1000); } fetchData((data) => { console.log(data); }); </code> 这里 `callback` 是一个函数参数,异步任务完成后被调用。 ==== 2. 回调的优点 ==== * 简单直接 * 容易理解 * 适合处理简单异步任务 ==== 3. 回调的问题 ==== 当多个异步任务相互依赖时,就容易出现“回调地狱”。 <code ts> setTimeout(() => { console.log("任务1完成"); setTimeout(() => { console.log("任务2完成"); setTimeout(() => { console.log("任务3完成"); }, 1000); }, 1000); }, 1000); </code> 这种嵌套层级很深,代码难以维护、难以扩展、错误处理也不统一。 ===== 五、Promise:异步编程的重要改进 ===== 为了解决回调函数的问题,ES6 引入了 `Promise`。TypeScript 对 Promise 提供了良好的类型支持。 ==== 1. Promise 的概念 ==== `Promise` 可以理解为“一个未来才会完成的结果”。 Promise 有三种状态: * `pending`:进行中 * `fulfilled`:已成功 * `rejected`:已失败 状态一旦从 `pending` 变为成功或失败,就不能再改变。 ==== 2. 创建 Promise ==== <code ts> const p = new Promise<string>((resolve, reject) => { const success = true; setTimeout(() => { if (success) { resolve("操作成功"); } else { reject("操作失败"); } }, 1000); }); </code> 这里的 `Promise<string>` 表示最终成功时返回的是字符串类型。 ==== 3. 使用 then 和 catch ==== <code ts> p.then((result) => { console.log("成功:", result); }).catch((error) => { console.error("失败:", error); }); </code> * `then` 处理成功结果 * `catch` 处理失败结果 ==== 4. 链式调用 ==== Promise 最大的优势之一就是链式调用。 <code ts> function step1(): Promise<number> { return Promise.resolve(1); } function step2(num: number): Promise<number> { return Promise.resolve(num + 1); } function step3(num: number): Promise<number> { return Promise.resolve(num + 1); } step1() .then((res) => step2(res)) .then((res) => step3(res)) .then((res) => { console.log("最终结果:", res); }) .catch((err) => { console.error(err); }); </code> 这种方式比多层回调更加清晰。 ===== 六、TypeScript 中 Promise 的类型系统 ===== TypeScript 相比 JavaScript 的优势,在于可以明确异步返回值类型。 ==== 1. 指定返回类型 ==== <code ts> function getUserName(): Promise<string> { return new Promise((resolve) => { setTimeout(() => { resolve("Alice"); }, 1000); }); } </code> 这里函数返回的不是 `string`,而是 `Promise<string>`。这表示“未来会得到一个字符串”。 ==== 2. 返回对象类型 ==== <code ts> type User = { id: number; name: string; }; function getUser(): Promise<User> { return Promise.resolve({ id: 1, name: "Tom" }); } </code> ==== 3. Promise<void> ==== 如果异步函数没有明确返回值,可使用 `Promise<void>`。 <code ts> function logAsync(): Promise<void> { return new Promise((resolve) => { setTimeout(() => { console.log("记录完成"); resolve(); }, 500); }); } </code> ==== 4. Promise<never> ==== 某些函数始终抛出错误或永远不会正常完成,可以涉及 `Promise<never>`,不过实际业务中使用较少。 ===== 七、async / await:更现代的异步写法 ===== `async / await` 是建立在 Promise 之上的语法糖,让异步代码看起来更像同步代码。 ==== 1. async 函数 ==== 只要在函数前加上 `async`,该函数就会返回一个 Promise。 <code ts> async function hello(): Promise<string> { return "Hello TypeScript"; } </code> 虽然直接返回的是字符串,但实际上会被包装成 `Promise<string>`。 ==== 2. await 的作用 ==== `await` 用于等待 Promise 完成,并获取其结果。 <code ts> function getData(): Promise<string> { return new Promise((resolve) => { setTimeout(() => { resolve("数据加载完成"); }, 1000); }); } async function main(): Promise<void> { const data = await getData(); console.log(data); } main(); </code> ==== 3. async / await 的优势 ==== * 代码可读性更强 * 更接近同步思维 * 便于调试 * 更适合复杂流程控制 ==== 4. 错误处理 ==== 使用 `try...catch` 捕获异步错误是 async/await 的常见方式。 <code ts> async function requestData(): Promise<void> { try { const result = await Promise.reject("请求失败"); console.log(result); } catch (error) { console.error("捕获到错误:", error); } } </code> ===== 八、Promise 与 async/await 的关系 ===== 很多初学者容易误以为 `async/await` 是另一套独立机制,实际上不是。 它们的关系可以总结为: * `async/await` 本质上是 Promise 的语法糖 * `await` 后面通常跟一个 Promise * `async` 函数本质上总是返回 Promise * 没有 Promise,就没有现代 async/await 异步模型 也就是说,学习 `async/await` 前,必须先掌握 Promise。 ===== 九、常用 Promise API ===== ==== 1. Promise.resolve ==== 快速创建一个成功状态的 Promise。 <code ts> const p = Promise.resolve(123); </code> ==== 2. Promise.reject ==== 快速创建一个失败状态的 Promise。 <code ts> const p = Promise.reject("出错了"); </code> ==== 3. Promise.all ==== 用于并发执行多个异步任务,全部成功才算成功。 <code ts> async function demo(): Promise<void> { const results = await Promise.all([ Promise.resolve("A"), Promise.resolve("B"), Promise.resolve("C") ]); console.log(results); } </code> 返回结果是数组: <code> ["A", "B", "C"] </code> 如果其中一个失败,则整体失败。 ==== 4. Promise.allSettled ==== 无论成功还是失败,都会等待全部任务完成。 <code ts> async function demo(): Promise<void> { const results = await Promise.allSettled([ Promise.resolve("成功1"), Promise.reject("失败1"), Promise.resolve("成功2") ]); console.log(results); } </code> 适合需要统计全部执行结果的场景。 ==== 5. Promise.race ==== 谁先完成,就返回谁的结果。 <code ts> async function demo(): Promise<void> { const result = await Promise.race([ new Promise((resolve) => setTimeout(() => resolve("快任务"), 500)), new Promise((resolve) => setTimeout(() => resolve("慢任务"), 1000)) ]); console.log(result); } </code> ==== 6. Promise.any ==== 返回第一个成功的 Promise,如果全失败才报错。 这在需要“多个备用接口取最快成功结果”时非常有用。 ===== 十、并发与串行 ===== 异步并不等于并发。理解串行与并发很重要。 ==== 1. 串行执行 ==== 前一个完成后再执行下一个。 <code ts> async function serial(): Promise<void> { const a = await Promise.resolve("A"); const b = await Promise.resolve("B"); const c = await Promise.resolve("C"); console.log(a, b, c); } </code> ==== 2. 并发执行 ==== 多个任务同时开始,再统一等待结果。 <code ts> async function parallel(): Promise<void> { const p1 = Promise.resolve("A"); const p2 = Promise.resolve("B"); const p3 = Promise.resolve("C"); const results = await Promise.all([p1, p2, p3]); console.log(results); } </code> 在彼此无依赖的情况下,并发通常效率更高。 ===== 十一、异步错误处理最佳实践 ===== ===== 1. 不要忽略 Promise 错误 ===== <code ts> async function badExample() { Promise.reject("错误"); } </code> 这样可能导致未处理的 Promise 异常。 ==== 2. 使用 try...catch ==== <code ts> async function goodExample(): Promise<void> { try { await Promise.reject("错误"); } catch (e) { console.error(e); } } </code> ==== 3. 合理定义错误类型 ==== 在 TypeScript 中,`catch` 捕获的错误通常建议先做类型收窄。 <code ts> try { throw new Error("出错"); } catch (e) { if (e instanceof Error) { console.log(e.message); } } </code> ===== 十二、异步函数的类型声明 ===== TypeScript 中可以为异步函数、回调函数、接口方法声明明确类型。 ==== 1. 函数类型 ==== <code ts> type AsyncFunc = (id: number) => Promise<string>; const fetchName: AsyncFunc = async (id) => { return `name-${id}`; }; </code> ==== 2. 接口中的异步方法 ==== <code ts> interface UserService { getUser(id: number): Promise<{ id: number; name: string }>; } </code> ==== 3. 泛型异步函数 ==== <code ts> async function wrap<T>(value: T): Promise<T> { return value; } </code> 泛型让异步函数更具复用性。 ===== 十三、实际开发中的常见场景 ===== ==== 1. 请求接口 ==== <code ts> type Post = { id: number; title: string; }; async function fetchPosts(): Promise<Post[]> { const response = await fetch("/api/posts"); const data = await response.json(); return data as Post[]; } </code> ==== 2. 延时工具函数 ==== <code ts> function sleep(ms: number): Promise<void> { return new Promise((resolve) => setTimeout(resolve, ms)); } async function demo(): Promise<void> { console.log("开始"); await sleep(1000); console.log("1秒后执行"); } </code> ==== 3. 重试机制 ==== <code ts> async function retry(fn: () => Promise<string>, count: number): Promise<string> { for (let i = 0; i < count; i++) { try { return await fn(); } catch (e) { if (i === count - 1) throw e; } } throw new Error("最终失败"); } </code> ===== 十四、常见误区 ===== * 误区一:`async` 函数返回普通值就不是 Promise 实际上仍然是 Promise。 * 误区二:`await` 可以阻塞整个程序 实际上它只是暂停当前 async 函数内部的执行。 * 误区三:用了 `await` 就一定更快 如果写成串行反而可能更慢。 * 误区四:Promise 和回调完全无关 实际上 Promise 内部很多时候仍然基于回调触发结果。 ===== 十五、总结 ===== TypeScript 的异步编程,核心可以归纳为以下几个层次: * 理解同步与异步的区别 * 掌握事件循环的基本原理 * 了解回调函数及其缺陷 * 熟练使用 Promise 处理异步结果 * 使用 `async/await` 编写更清晰的异步代码 * 学会并发控制与错误处理 * 利用 TypeScript 类型系统为异步函数提供更安全的约束 如果把异步知识体系看作一个成长路径,那么可以概括为: - 回调函数是起点 - Promise 是核心 - async/await 是实践中的主流写法 - 类型系统让异步代码更加可靠、可维护 在实际开发中,建议优先使用 `async/await` 编写业务逻辑,同时理解 Promise 的底层思想;在需要并发时配合 `Promise.all`、`Promise.allSettled` 等 API 使用。只有把“运行机制 + Promise + async/await + 类型约束”四者结合起来,才能真正掌握 TypeScript 异步编程。 如果你愿意,我还可以继续为你输出一份: * **更适合直接粘贴到 DokuWiki 的排版优化版** * **带面试题的 TypeScript 异步专题版** * **包含微任务、宏任务、事件循环深入分析的进阶版** 你只要回复一句:**继续扩展** 即可。 登录 Detach Close 该主题尚不存在 您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。 typescript/异步.txt 最后更改: 2026/03/09 16:38由 张叶安 登录