
在 JavaScript 异步编程中,async/await 语法极大地简化了基于 Promise 的异步操作。然而,在某些特定场景下,其行为可能与直觉不符,尤其是在涉及错误处理时。理解 await 的底层机制,特别是它如何处理同步抛出的错误,对于编写健壮的异步代码至关重要。
async/await 的基本工作原理
首先,回顾 async 函数和 await 表达式的基础。async 函数总是返回一个 Promise。await 关键字只能在 async 函数内部使用,它会暂停 async 函数的执行,直到其操作数(一个 Promise)被解决(resolved)或拒绝(rejected)。根据 MDN 文档,await 表达式的操作数会通过 Promise.resolve() 的方式被解析:它总是被转换为一个原生的 Promise,然后才被 await。这意味着,即使 await 后面的表达式不是一个 Promise,它也会被隐式地包装成一个已解决的 Promise。
await 行为的边界:同步抛错的特殊性
我们通过以下几个示例来深入探讨 await 在不同错误处理场景下的行为差异。
场景一:异步函数内部抛出错误
当 await 操作一个由 async 函数返回的 Promise,即使该 async 函数内部抛出错误,await 也会以异步方式处理。这是因为 async 函数在抛出错误时,会隐式地返回一个被拒绝的 Promise。
立即学习“Java免费学习笔记(深入)”;
async function load(closure) {
try {
await closure();
} catch (error) {
console.log("error");
} finally {
console.log("finished");
}
}
// 示例 1: closure 是一个 async 函数,抛出错误
load(async () => {
throw new Error("Async error");
});
console.log("hello");输出:
hello error finished
解析: 在这个例子中,closure 是一个 async 函数。当它执行 throw new Error() 时,closure 函数会返回一个被拒绝的 Promise。load 函数中的 await closure() 接收到这个被拒绝的 Promise 后,会暂停 load 函数的执行,并将控制权交回事件循环。因此,console.log("hello") 会立即执行。在下一个事件循环周期,await 捕获到 closure Promise 的拒绝,执行 catch 块,然后是 finally 块。
场景二:非异步函数同步抛出错误
这是导致行为差异的关键场景。当 await 操作一个非异步函数,且该函数在返回任何值(包括 Promise)之前就同步抛出了错误,await 将无法介入。
async function load(closure) {
try {
await closure(); // 这里的 await 行为是关键
} catch (error) {
console.log("error");
} finally {
console.log("finished");
}
}
// 示例 2: closure 是一个普通函数,同步抛出错误
load(() => {
throw new Error("Synchronous error");
});
console.log("hello");输出:
error finished hello
解析: 在这个例子中,closure 是一个普通的同步函数。当 load 函数内部调用 closure() 时,closure() 会立即执行 throw new Error()。这个错误是在 await 表达式有机会将 closure() 的返回值(如果它有返回值的话)转换为 Promise 之前 同步发生的。因此,await 根本没有机会接收到任何 Promise 来进行异步暂停。错误会立即沿着调用栈向上冒泡,被 load 函数的 try...catch 块捕获。整个 try...catch...finally 流程都是同步完成的,然后 load 函数才返回。此时,console.log("hello") 才会执行。
场景三:非异步函数同步返回值
为了进一步对比,我们看一个非异步函数返回一个值的例子。
async function load(closure) {
try {
await closure(); // 1 会被 Promise.resolve(1) 转换为已解决的 Promise
} catch (error) {
console.log("error"); // 不会执行
} finally {
console.log("










