如何让rejected Promise与resolved Promise按相同时间顺序捕获?Error对象reject时为何顺序异常?
Promise拒绝操作使用Error对象时时序晚于成功操作的原因
调用resolve()或reject()的Promise无论处于完成还是拒绝状态,都可通过.then()按顺序处理:
const getRecordById = (id) => { return new Promise((resolve, reject) => { const randNum = Math.floor(Math.random() * 2) + 1; if (randNum === 1) { resolve({ id, firstName: 'John', lastName: 'Doe' }); } else { reject(`user id ${id} not found`); } }); }; for (let id = 1; id <= 10; id++) { getRecordById(id) .then( (record) => { console.log(`you got record #${record.id}`); }, (errMessage) => { console.log(`error was: ${errMessage}`); } ); }

通用开发规范建议:
多数库和框架都默认Promise始终使用Error对象抛出拒绝结果
因此建议使用reject(new Error('Oops!'));的形式抛出拒绝结果,而非直接返回字符串。
但修改为Error对象抛出后,会观察到所有拒绝操作的完成时间都晚于所有成功resolve操作:
const getRecordById = (id) => { return new Promise((resolve, reject) => { const randNum = Math.floor(Math.random() * 2) + 1; if (randNum === 1) { resolve({ id, firstName: 'John', lastName: 'Doe' }); } else { reject (new Error(`user id ${id} not found`)); } }); }; for (let id = 1; id <= 10; id++) { getRecordById(id) .then((record) => { console.log(`you got record #${record.id}`); }) .catch((errMessage) => { console.log(`error was: ${errMessage}`); }); }
原因说明
该现象并非Promise规范的强制要求,属于多重因素叠加的偶发现象:
- 回调链式层级差异:第一段代码的错误回调和成功回调是
then的同级参数,Promise敲定状态后会直接将对应回调推入微任务队列,时序完全由Promise敲定时机决定;第二段代码使用then+catch的链式写法,catch是额外的一层Promise回调,拒绝状态需要先跳过then的成功回调,再将catch回调推入队列,天然比同级成功回调晚一个微任务周期。 - Error实例的创建开销:创建Error对象时,JS引擎需要收集当前的调用堆栈信息,该操作的性能开销远高于直接生成字符串,会导致
reject的触发时机比resolve慢数毫秒,并发场景下就容易出现成功回调批量先执行的情况。 - 随机数概率巧合:示例中使用1~2的随机数判断状态,多次运行刚好出现更多
randNum=1的结果也属于正常概率范围,和是否使用Error对象没有直接关联。
内容的提问来源于stack exchange,提问作者Edward Tanguay
相关产品推荐
相关产品推荐

