JavaScript消息队列run-to-completion机制与event loop永不阻塞是否矛盾?
这两个特性完全不矛盾,二者描述的是JavaScript event loop 在不同阶段、不同场景下的行为规则,适用边界完全不重叠。
1. 什么是run-to-completion(运行至完成)
这个规则约束的是已经进入JS主线程调用栈、正在执行的同步代码块的执行逻辑:
- 只要某个任务(消息队列里取出的回调对应的代码块)开始执行,就会完整运行完所有同步代码才会处理下一个任务,执行过程中不会被其他JS代码抢占打断
- 比如你写了一个遍历1亿条数据的同步循环,执行过程中用户点击、定时器回调这类任务都只能在队列里排队,必须等循环跑完才会被处理
- 这个规则的好处是保证了代码执行的原子性,不用处理多线程抢占资源导致的数据状态混乱问题;缺点就是如果单块同步代码耗时太长,会卡住后续所有任务,导致页面交互无响应,浏览器也会因此弹出脚本运行超时的提示。
2. 什么是「永不阻塞」
这个特性描述的是JS主线程处理异步任务时的等待逻辑:
- JavaScript的IO类操作(比如网络请求、IndexedDB读写、文件读取等)都是交给浏览器的其他后台线程处理的,主线程发起异步请求后不会原地等待操作结果,而是会继续处理消息队列里的其他就绪任务
- 只有等异步操作完成后,对应的回调才会被插入消息队列,等到主线程调用栈为空时再取出执行
- 比如你发起一个需要5秒返回的网络请求,这5秒里主线程完全可以正常响应用户的点击、滚动操作,不会因为等请求结果就停下所有工作,这就是「永不阻塞」的体现。
为什么二者不冲突?
举个很直观的例子就懂了:
- 你发起一个3秒返回的网络请求,这3秒里主线程正常响应用户操作,不会卡,这是永不阻塞的表现
- 3秒后请求返回,回调函数进入队列,等到主线程空闲时开始执行这个回调。如果回调里有一段需要2秒才能跑完的同步计算逻辑,这2秒里所有其他任务都得排队等这段逻辑跑完,不会被中途打断,这是run-to-completion的表现
两个规则针对的是完全不同的阶段,没有任何矛盾的地方。前者保障了同步代码执行的可预测性,后者保障了异步场景下主线程的响应效率。
内容的提问来源于stack exchange,提问作者Ha0ran
相关产品推荐
相关产品推荐

