跳到主要内容

2.3 模块、Promise 与异步

本章目标: 拆分 TypeScript 文件,并正确等待异步结果、处理失败。

用模块组织代码​

src/utils/task.ts
export function normalizeTitle(title: string) {
return title.trim();
}
App.vue 脚本区域中的导入
import { normalizeTitle } from './utils/task';

console.log(normalizeTitle(' 学习 Vue '));

具名导出使用 { 名称 },默认导出通常不带花括号。Vue 单文件组件一般按默认导入使用:import TaskRow from './components/TaskRow.vue'。

对照 using

import 会建立模块依赖,并绑定导出的值;它不只是 C# using 那样引入命名空间。构建工具据此打包代码。

Promise 与 Task​

Promise<T> 表示将来成功得到 T,或者失败。async 函数总返回 Promise,await 暂停当前异步函数的后续执行,不会阻塞整个浏览器。

异步示例片段
function delay(milliseconds: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, milliseconds));
}

async function loadTitle(): Promise<string> {
await delay(500);
return '学习 Vue 3';
}

async function run() {
try {
const title = await loadTitle();
console.log(title);
} catch (error) {
console.error('加载失败', error);
} finally {
console.log('本次操作结束');
}
}

void run();

void run() 只是明确忽略返回值,不会捕获异常。这里安全是因为 run 内部已有 try/catch。

理解执行顺序​

console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');

结果为 A → D → C → B。同步代码先执行;Promise 回调进入微任务队列;定时器回调随后作为任务运行。浏览器中的 JS 主线程与 .NET 线程池的模型不同。

await 不会把 CPU 运算自动放到后台

一个运行很久的同步循环仍会卡住页面。CPU 密集任务可以考虑 Web Worker;网络等待本身不需要为每个请求创建一个线程。

串行与并行等待​

互不依赖的请求可以一起发起:

假设 loadTasks 和 loadProfile 已定义的调用片段
const [tasks, profile] = await Promise.all([
loadTasks(),
loadProfile(),
]);

Promise.all 的一个请求失败会让整体拒绝,但不会自动取消其他请求。需要独立汇总成功和失败时考虑 Promise.allSettled。

不要写 tasks.forEach(async task => ...) 后就认为所有操作已经完成。forEach 不等待回调返回的 Promise。串行用 for...of + await;并行用 Promise.all(tasks.map(...))。

网络请求的两个失败层次​

读取 JSON 的基础片段
async function getTasks() {
const response = await fetch('/api/tasks');
if (!response.ok) {
throw new Error(`请求失败:HTTP ${response.status}`);
}
return await response.json();
}

网络中断会使 fetch 拒绝;HTTP 400、404、500 通常仍得到 Response,要自己检查 ok。这里的返回数据没有类型验证,完整处理见 HTTP 请求。

练习​

预测下列代码输出顺序,然后运行验证:

async function example() {
console.log(1);
await Promise.resolve();
console.log(2);
}
example();
console.log(3);
参考答案

输出 1、3、2。await 后面的部分会在同步调用栈结束后继续执行,即使等待的 Promise 已经兑现。

参考:MDN Promise、模块。