JavaScript并发异步操作中setInterval与Promise执行逻辑验证问题
你的结论完全正确,这个现象由JavaScript单线程事件循环的执行规则决定,没有问题。
核心逻辑验证
JavaScript运行时是单线程的,所有同步代码、微任务、宏任务的执行遵循以下优先级:
- 先执行当前调用栈的所有同步代码
- 清空全部微任务队列
- 再取宏任务队列的最早一个任务执行
两个setInterval注册的回调都属于宏任务,必须等当前执行上下文清空后才会被调度,不可能打断正在运行的同步代码。
第一个版本无报错的原因
你最初的b回调逻辑:
const b = setInterval(async () => { await new Promise(resolve => setTimeout(resolve, 500)); total = [...total, ...buffer]; buffer = []; }, 1);
执行流程是:
- b的回调触发后,第一行遇到
await,直接让出线程,此时a的回调可以正常被调度,持续往buffer里push递增的count值 - 500ms后
await后的逻辑被推入微任务队列,此时引擎会先清空微任务再处理下一个宏任务:total = [...total, ...buffer]和buffer = []是连续的同步代码,执行过程中不会被a的宏任务打断- 没有新的count会在拷贝和清空buffer的间隙被push进buffer,因此total里的数值完全连续,不会出现不匹配的报错
修改后出现报错的原因
你调整后的b回调逻辑:
const b = setInterval(async () => { total = [...total, ...buffer]; await new Promise(resolve => setTimeout(resolve, 500)); buffer = []; }, 1);
执行流程出现了断层:
- b的回调触发后,先同步把当前buffer的内容拷贝到total,然后才遇到
await让出线程 - 接下来的500ms里,a的回调持续往buffer里push新的count值,这些值没有被拷贝到total
- 500ms后执行
buffer = [],直接清空了这500ms里新增的未被记录的count值,导致total里的数值出现断档,自然会触发Error on x的日志,刚好反向验证了你之前的结论。
内容的提问来源于stack exchange,提问作者Corentin Bernard
相关产品推荐
相关产品推荐

