Promise.race异步调度机制与微任务优先级
Promise.race 这个 API,许多人第一反应会把它与 .then() 划等号,误以为它也会生成微任务。实际上并不如此——它本身并不创建微任务,只是同步监听多个 Promise 的首个状态变化,然后把那个结果(无论是 fulfilled 还是 rejected)直接“透传”给它的返回 Pro
Promise.race 这个 API,许多人第一反应会把它与 .then() 划等号,误以为它也会生成微任务。实际上并不如此——它本身并不创建微任务,只是同步监听多个 Promise 的首个状态变化,然后把那个结果(无论是 fulfilled 还是 rejected)直接“透传”给它的返回 Promise。真正决定调度顺序的,是被 race 的那些 Promise 内部的状态变更时机,而后续的 .then() 回调,依然是标准的微任务。

Promise.race 不是微任务的发起者
与 .then() 不同,Promise.race() 调用本身是同步执行的。它不会立即往微任务队列里塞任何回调。它只做一件事:为传入的每个 Promise 实例注册一个内部“完成监听器”。当其中任意一个 Promise 状态变为 fulfilled 或 rejected 时,race 返回的那个新 Promise 就会以相同结果立即 settle(即进入 fulfilled 或 rejected 状态)。这个 settle 动作会触发后续链式调用(比如 .then())的回调——而这些回调,才是真正的微任务。
实际执行顺序由被 race 的 Promise 决定
race 的“竞速”结果,完全取决于哪个被监听的 Promise 最先完成其状态变更逻辑。关键点在于:
- 如果某个 Promise 在构造函数中直接
resolve('a'),那它属于同步 resolve,其.then()回调会在当前宏任务末尾、微任务阶段执行; - 如果另一个 Promise 是
setTimeout(() => resolve('b'), 0),那它的 resolve 会发生在下一个宏任务,因此它的.then()一定晚于前者; - 所以
Promise.race([syncP, timeoutP])的结果一定是 syncP 的值,且 race 返回的 Promise 的.then()会排在本轮微任务队列靠前位置。
race 后续的 .then 是标准微任务
一旦 race 返回的 Promise 状态确定,你对其调用的 .then()、.catch() 就和普通 Promise 一样:回调被放入微任务队列,等待当前同步代码执行完毕后统一清空。例如:
console.log('1');
Promise.race([
Promise.resolve('fast'),
new Promise(r => setTimeout(() => r('slow'), 10))
]).then(v => console.log('2:', v));
console.log('3');
输出顺序是 1 → 3 → 2: fast,因为 .then() 是微任务,严格遵循“同步 → 微任务 → 宏任务”的事件循环规则。
常见误用:把 race 当作“强制提前结束”工具
需要警惕的是,race 不会取消或中断其他仍在运行的 Promise。它只关心“谁第一个完成”,其余 Promise 继续执行,可能产生副作用(比如多余的网络请求、未清理的定时器)。若需真正取消,应配合 AbortController 或手动设计可取消逻辑,不能依赖 race 本身。
