标准答案

  1. 状态:Promise 有 pending、fulfilled、rejected 三种状态。状态只能从 pending 变成 fulfilled 或 rejected,一旦 settled 就不可逆,后续再次 resolve 或 reject 都不会改变结果。
  2. 链式调用:then、catch、finally 都会返回新的 Promise。then 回调返回普通值时,下一个 then 收到这个值;返回 Promise 或 thenable 时,下一个节点会等待它的状态和值。
  3. 错误穿透:错误会沿 Promise 链向后传播,直到被 catch 或 then 的 rejection 回调处理。catch 本质上是 then(null, onRejected),处理后如果返回正常值,链路会回到 fulfilled。
  4. finally:finally 适合做清理动作,不接收上一步业务结果,默认也不改变原来的值或错误。但如果 finally 内部抛错或返回 rejected Promise,后续链路会变成 rejected。

题目解析

Promise 的解析重点是“状态”和“链”。只说 pending、fulfilled、rejected 不够,还要说明状态不可逆,以及 then/catch/finally 每次都会返回新的 Promise。

then 返回新 Promise 以后,链式调用才能把普通值、抛出的错误、返回的 Promise 统一串起来。这个点解释清楚后,错误穿透、值传递、异步等待就都能顺下来。

finally 经常被误解成能拿到结果或修改结果。更准确地说,它主要用于清理,默认透传原来的值或错误;但它自己抛错或返回 rejected Promise 时,会覆盖后续链路状态。

Promise.resolve、thenable 和手写 Promise 会继续涉及“状态吸收”和“回调队列”。这些内容属于进一步展开,基础层面先把状态流转和值传递说清楚。

代码示例

这个例子能同时说明值传递、错误穿透和 finally 的清理语义。

JavaScript
Promise.resolve(1)
  .then(value => value + 1)
  .then(value => {
    throw new Error('failed')
  })
  .then(() => {
    console.log('skipped')
  })
  .catch(error => {
    console.log(error.message) // failed
    return 10
  })
  .finally(() => {
    console.log('cleanup')
  })
  .then(value => {
    console.log(value) // 10
  })

常见误区

  • 误以为 Promise 状态可以多次改变。
  • 忘记 then/catch/finally 都返回新的 Promise。
  • 误以为 finally 能拿到上一步的业务结果。

作者信息