await 会将具有 then 方法的“类 promise”对象识别为 thenable 并按 promises/a+ 规范处理;v8 通过轻量探测(对象/函数且 then 可调)动态提取并调用 then,支持 getter、proxy 等响应式场景,但性能低于原生 promise。

await 会把后面表达式当作“类 Promise”来处理,只要它有符合规范的 then 方法,V8 就按 Promises/A+ 的鸭子类型逻辑执行——不看构造函数,只看行为。
thenable 的识别条件
V8 在遇到 await x 时,并不会检查 x 是否是 Promise 实例,而是做一次轻量探测:
-
x是对象或函数(非 null、undefined、原始值) -
x.then是一个可调用函数(typeof === 'function')
满足这两点,V8 就认定它是 thenable,并进入“扁平化 + 状态等待”流程。
动态提取 then 方法的过程
V8 不会提前缓存 x.then,而是在每次 await 解析时实时读取:
- 先取
x.then当前值(支持 getter、代理、运行时覆盖) - 立即调用它,传入
resolve和reject两个回调 - 若调用成功且未抛错,就等待这两个回调之一被调用
这意味着 then 可以是响应式属性、Proxy 拦截器,甚至每次返回不同函数——V8 都能正确响应。
和 Promise 构造的本质区别
普通 Promise 实例在创建时就绑定状态机;而 thenable 是“惰性接入”:
- 没有内部
[[PromiseState]]属性 - 不参与 V8 的隐藏类优化路径(无固定属性布局)
- 每次 await 都触发完整 then 查找 + 函数调用开销
所以性能敏感场景应避免频繁 await 动态 thenable,优先用原生 Promise 或 Promise.resolve(x) 显式转换。
一个典型可运行例子
下面代码在 V8 中能正常 await,且输出顺序符合微任务调度:
const obj = {<br> get then() {<br> console.log('then 被读取');<br> return (resolve) => {<br> console.log('then 执行');<br> setTimeout(() => resolve('done'), 0);<br> };<br> }<br>};<br><br>(async () => {<br> console.log('start');<br> const res = await obj;<br> console.log(res); // 'done'<br>)();
输出为:start → then 被读取 → then 执行 → done,全程由 V8 的微任务机制保障顺序。











