异步逻辑中try-catch不创建新作用域,但需await或.catch()才能捕获promise拒绝;变量在async函数中由闭包维持,try内声明的变量在catch/finally中仍可达,嵌套异步需显式await确保错误被捕获。

在异步逻辑中,try-catch 本身**不创建新作用域**,也不会改变变量的作用域链,但它会影响错误发生时的执行流和变量的“可见性窗口”,进而间接影响状态引用是否有效。关键不在语法作用域,而在**执行时机与闭包生命周期的配合**。
异步代码里 try-catch 不捕获未 await 的 Promise 拒绝
这是最常见误解的根源:如果只写 try { fetch('/api') } catch(e) {...},而没 await 或 .catch(),Promise 拒绝不会进入 catch 块——它会被当作未处理拒绝(unhandled rejection),根本进不了你写的 catch。
-
错误写法:Promise 被抛出但未等待,
catch形同虚设 -
正确写法:必须
await或链式.catch(),才能让异常落入catch处理范围 - 此时
try块内声明的变量(如const res = await fetch(...))仍保留在闭包中,可被catch或后续逻辑安全访问
await 表达式中的变量引用由闭包维持,不受 try-catch 干扰
try 块内部用 let 或 const 声明的变量,在 await 暂停后依然可达——因为 async 函数会自动保留当前词法环境,形成闭包。
- 例如:
let id = 123; try { await api.update(id); } catch(e) { console.log(id); }——id在catch中完全可用 - 这和普通函数调用暂停后恢复执行的机制一致,不是
try-catch的功劳,而是async函数的固有特性 - 若变量在
try外声明,或在catch内重新声明,则需注意遮蔽(shadowing)问题
finally 中访问变量需确认其声明位置和生存期
finally 总会执行,但它能访问哪些变量,取决于这些变量是否在 try 块作用域内声明,以及是否已被垃圾回收。
- 在
try中用const data = await getData();声明 →data可在finally中读取(只要未被提前释放) - 若数据是大型对象且不再需要,可在
try结束前手动置为null,避免finally意外延长其生命周期 - 注意:
finally里不能return覆盖try或catch的返回值(JS 中会覆盖;但语义上易引发意外)
嵌套 async 函数 + try-catch 容易丢失外层变量引用
当在 try 块中定义并立即调用另一个 async 函数,而该函数出错时,错误可能无法被当前 catch 捕获——除非显式 await 或处理其返回的 Promise。
- 例如:
try { someAsyncTask().catch(handleErr); }→ 错误被内部处理,不会触发外层catch - 若希望统一兜底,应确保所有异步路径都汇聚到同一 Promise 链,或使用
Promise.allSettled等可控结构 - 此时外层
try中的变量仍存在,但逻辑上已“脱离上下文”,容易造成状态不一致











