iOS 14.1与16.x中Promise.resolve().then()执行顺序差异排查及解决
不同iOS版本下Promise回调执行顺序差异问题解析
测试代码
const resolve = Promise.resolve() function run() { try { try { console.log(1) resolve.then(() => { console.log(resolve, 2) }) console.log(3) } finally { console.log(4) } } catch (e) { console.log(e) } console.log('return') } run()
不同iOS版本的执行结果
- iOS 14.1 输出:
1 3 2 4 return
- iOS 16.x 输出:
1 3 4 return 2
问题
为何会出现这种情况?该问题的根本原因是什么,又该如何解决?
根本原因
这是iOS不同版本搭载的JavaScriptCore(WebKit内置JS引擎)对已决议Promise的then回调处理逻辑不一致导致的:
- iOS 14的旧版引擎中,调用已决议Promise的
then时,回调会被插入到微任务队列的队首,优先级高于当前执行栈中待执行的同步代码(比如finally块),导致回调在finally之前触发。 - iOS 16的新版引擎修正了这个行为,严格遵循ECMAScript标准:已决议Promise的
then回调会被正常加入微任务队列末尾,必须等当前执行栈中的所有同步代码(包括finally块、函数末尾的console.log('return'))执行完毕后,才会依次执行微任务队列中的回调。
本质是旧版引擎的实现不符合ES标准,新版做了标准对齐。
解决方法
要让代码在所有iOS版本中保持一致的执行顺序,需保证Promise回调的执行时机符合标准,推荐两种方案:
方案一:用新Promise包裹,强制标准异步顺序
将已决议Promise的回调逻辑放入新的Promise.resolve()的then中,确保回调进入正确的微任务队列顺序:
const resolve = Promise.resolve() function run() { try { try { console.log(1) // 用新Promise包裹,让回调按标准微任务顺序执行 Promise.resolve().then(() => { resolve.then(() => { console.log(resolve, 2) }) }) console.log(3) } finally { console.log(4) } } catch (e) { console.log(e) } console.log('return') } run()
修改后所有版本都会输出:
1 3 4 return 2
方案二:明确同步与异步边界
如果需要调整回调的执行位置(比如在finally之后、return之前),可以显式控制代码顺序,比如将finally的部分逻辑或回调逻辑放入微任务队列,但这种场景下方案一更直接有效。
内容的提问来源于stack exchange,提问作者Menyou
相关产品推荐
相关产品推荐

