驾驭事件循环,正确地并行化你的 promise,并让异步错误处理变得可靠。
在 Kodokon 中打开本课执行模型依赖于一个宏任务队列(setTimeout、I/O)和一个微任务队列(.then、await、queueMicrotask)。在每个任务之后,引擎会在继续之前彻底清空微任务队列。直接后果是:一个已解决的 promise 总是先于 setTimeout(fn, 0) 运行,而一条过长的微任务链条完全可能像同步循环一样冻结渲染。
console.log("start");
setTimeout(() => console.log("macrotask"), 0);
Promise.resolve().then(() => console.log("microtask"));
console.log("end");
// start, end, microtask, macrotaskasync/await 是构建在 promise 之上的语法糖,但它隐藏着一个性能陷阱:循环内部的每个 await 都会把这些操作串行化。如果这些请求是相互独立的,就先把它们全部发起,然后用 Promise.all 等待整批完成。对于三个各 100 毫秒的请求,串行版本要花 300 毫秒,而并行版本只要 100 毫秒。
const fetchUser = (id) => new Promise((res) => {
setTimeout(() => res({ id }), 100);
});
async function loadSequential(ids) {
const users = [];
for (const id of ids) {
users.push(await fetchUser(id)); // ~100 ms each
}
return users;
}
async function loadParallel(ids) {
return Promise.all(ids.map(fetchUser)); // ~100 ms total
}根据你所需要的失败契约来挑选合适的组合器:Promise.all 在第一个失败时就拒绝(全有或全无),allSettled 会等待所有人并描述每一个结果,race 取第一个敲定的结果(无论成功或失败,适合用于超时),any 取第一个成功的结果,只有在全部失败时才拒绝。对于一个必须展示部分结果的仪表盘,allSettled 几乎总是正确的选择。
const tasks = [
Promise.resolve("profile loaded"),
Promise.reject(new Error("network unavailable")),
];
const results = await Promise.allSettled(tasks);
for (const r of results) {
if (r.status === "fulfilled") {
console.log("success:", r.value);
} else {
console.error("failure:", r.reason.message);
}
}console.log、一个 Promise.resolve().then 回调和一个 setTimeout(fn, 0) 回调,会按什么顺序运行?try { doAsync(); } catch (e) {} 无法捕获 doAsync 的拒绝?