Promise对象如何简化javascript异步流程【教程】

Promise 用于组织异步依赖而非简化流程;then 链明确执行顺序而非仅减少嵌套;滥用致错误静默、调试困难;async/await 是语法糖,底层仍依赖 Promise;需依数据依赖关系选择 await、Promise.all 或 allSettled。

Promise 不是用来“简化”异步流程的,而是用来“组织”和“表达”异步依赖关系的——用错方式反而会让代码更难懂。

Promise.then() 链式调用不是为了减少嵌套,而是为了明确执行顺序

很多人以为 then() 是为了解决回调地狱(callback hell),但实际中

滥用链式调用会导致错误被静默吞掉、调试困难、逻辑分支混乱。

  • 每个 then() 只接收上一个成功返回的值,失败不会自动向下传递,除非显式 return Promise.reject(...) 或抛出异常
  • 如果某个 then() 里忘记 return,后续 then() 会收到 undefined,而非预期数据
  • 多个并行请求不要强行塞进一条链,该用 Promise.all([p1, p2]) 就别写 p1.then(() => p2)

async/await 是 Promise 的语法糖,不是替代品

async/await 看起来更“同步”,但底层完全依赖 Promise。它解决的是书写形式问题,不改变异步本质。

  • await 只能用在 async 函数内,否则报错 SyntaxError: await is only valid in async functions
  • await 后面如果不是 Promise,会被自动包装成 Promise.resolve(value),这点常被忽略导致时序误判
  • 想捕获错误必须用 try/catch,而不是靠 .catch(),二者作用域不同

Promise.race() 和 Promise.allSettled() 容易用反

这两个方法名字相似,行为却相反:一个看“谁最快”,一个看“谁全结束”。选错会直接导致业务逻辑出错。

  • Promise.race([p1, p2]) 在任一 Promise settle(fulfill 或 reject)时就结束,哪怕那个是失败的 —— 常见于超时控制,但若没处理 reject,就会抛未捕获异常
  • Promise.allSettled([p1, p2]) 总是等待全部完成,返回每个 Promise 的状态对象({status: 'fulfilled', value}{status: 'rejected', reason}),适合“不管成败都要汇总结果”的场景
  • Promise.all() 一旦有任意一个 reject 就立刻 reject,适合“全成功才继续”的原子操作,比如批量提交表单

reject 不等于 throw,catch 也不等于 try/catch

Promise 构造函数里的 throw 会被自动转为 reject,但普通同步错误(如访问 undefined.x)不会自动进入 .catch(),除非发生在 Promise 执行器或 then() 回调中。

  • 构造 Promise 时,new Promise((resolve, reject) => { throw new Error('oops') }) 等价于 reject(new Error('oops'))
  • 但在 then() 外部抛错(比如 console.log(x.y); then(...)),这个错误跟 Promise 链无关,.catch() 捕不到
  • 浏览器中未被 catch() 的 Promise rejection 会触发 unhandledrejection 事件,Node.js 则直接退出进程(v15+)

真正难的从来不是怎么写 Promise,而是判断哪一步该用 await、哪一步该用 Promise.all、哪一步根本不该封装成 Promise —— 这些取决于你对数据依赖关系的真实理解,而不是语法是否“看起来简洁”。