JavaScript异步安全的核心是确保异常被看见、捕获和响应,避免静默丢失;需统一兜底错误处理、正确使用Promise链与async/await、防范共享状态竞态、关键操作前重验上下文并防重复提交。

JavaScript 异步执行的安全性,核心不在“能不能异步”,而在于异常是否被真正看见、捕获和响应。很多线上故障不是因为逻辑写错,而是因为错误静默丢失——用户点击没反应、接口看似成功却没生效、日志里查不到报错,背后往往是异步异常处理失效。
别让异常掉进“无人区”
同步代码中 throw 一个错误,会立刻中断执行并冒泡;但异步中抛出的错误(比如在 setTimeout、Promise 回调或 fetch.then 里)不会自动上浮到外层 try-catch,而是被封装进 Promise 实例,若没人主动检查,它就一直“挂起”,最终变成未处理拒绝(unhandledrejection),既不报错也不告警。
- 避免写 async () => { throw new Error(...) } 却不 await 或不加 .catch()
- 所有顶层异步入口(如按钮点击事件、组件挂载时的初始化请求)必须包裹统一错误兜底逻辑,至少记录日志 + 显示用户提示
- 前端全局监听 window.addEventListener('unhandledrejection', ...),捕获漏网的 Promise 拒绝,用于监控和告警
Promise 链不能断,catch 要落在最后
Promise 的 .catch() 只捕获前面链路上的 rejected 状态,一旦中间用了 .then() 但没返回新 Promise,或者写了空的 .then(() => {}),错误就会中断传播,后续 .catch() 就收不到。
- 每个 .then() 处理器都应明确返回值:成功返回数据,失败用 throw 或 return Promise.reject() 主动传递错误
- 不要省略 .catch() —— 即使只是 .catch(err => { console.error(err); }),也比不写强
- 使用 async/await 时,try/catch 必须包住整个 await 表达式,而不是只包 await 后面那一行
避免共享状态被并发修改
JS 虽是单线程,但多个 Promise 回调可能按不可预测顺序执行,若共用一个变量(比如计数器、开关标志、临时缓存),极易出现“读-改-写”竞态:两个回调同时读到旧值,各自+1后再写回,结果只加了一次。
立即学习“Java免费学习笔记(深入)”;
- 禁用裸变量赋值:不要直接写 count++ 或 user.status = 'loading'
- 优先用不可变更新:如 setState(prev => ({ ...prev, loading: true }))(React)或 state.update(old => ({ ...old, count: old.count + 1 }))
- 对必须修改的共享状态,用原子操作兜底:如 localStorage 更新前先 get + JSON.parse,变更后 set;或借助 IndexedDB 的事务机制
敏感操作必须重验上下文
异步延迟执行放大了越权风险——比如用户点击删除按钮后立即登出,但删除请求还在队列里;或短信验证码校验通过后,攻击者复用同一请求多次触发发送。
- 关键操作(如支付、删库、发验证码)发起前,重新校验登录态、权限、CSRF Token 和操作时效(≤60s)
- 避免把用户身份信息存在闭包或局部变量里传进异步回调;每次进入回调都从可信源(如 auth store、token payload)实时取
- 对重复提交做防抖:按钮点击后置为 disabled,或用唯一请求 ID + 后端幂等键控制


















