await后跟非promise值合法,引擎自动调用promise.resolve()将其转为已兑现promise,行为上仍走微任务队列,实现同步值的异步化统一处理。

不会报错,也不会卡住——await 后面跟非 Promise 值是完全合法的,JavaScript 会自动处理,但这个“自动”有明确规则,不是凭空消失或跳过。
它其实做了这一步:Promise.resolve()
无论你写的是 await 42、await "ok"、await null 还是 await {x: 1},引擎内部都会悄悄执行:
-
Promise.resolve(42)→ 得到一个已兑现(fulfilled)的 Promise,值为42 -
Promise.resolve("ok")→ 得到一个已兑现的 Promise,值为"ok" -
Promise.resolve(null)→ 得到一个已兑现的 Promise,值为null
所以 await 等待的**永远是一个 Promise**,只是这个 Promise 是 JS 自动帮你造出来的,且状态立刻为 fulfilled。
执行效果:同步值变“微任务延迟”
虽然结果是立即可知的,但行为上仍走异步流程:
- 代码不会阻塞主线程,但当前 async 函数会暂停,交出控制权
- 该 await 表达式会被推入微任务队列,等本轮同步任务结束后才继续执行
- 因此
console.log(await "hello")一定晚于同层的同步console.log("world")
和真 Promise 的关键区别
表面上看结果一样,但底层调度不同:
-
await fetch("/api"):真正等待网络响应,可能耗时几百毫秒甚至更久 -
await "hello":不等待外部事件,只等微任务队列轮到它,延迟通常在几微秒级 - 多次 await 非 Promise 值会累积微任务开销,虽小但在高频循环中可能被感知
为什么允许这样设计?
核心是为了统一处理逻辑:
- 函数返回值可能是 Promise,也可能是同步计算结果(比如缓存命中直接返回对象)
- 用
await统一接住,不用每次判断if (val instanceof Promise) - 让调用方无需关心上游是同步还是异步,接口更健壮
不过要注意:这种便利性不等于鼓励滥用。若明知是同步值,还刻意写 await someSyncValue,会降低可读性,也掩盖了真实的数据流节奏。











