javascript - Event Loop 中异步任务的疑问◔ ‸◔?
ringa_lee
ringa_lee 2017-04-11 13:15:04
[JavaScript讨论组]

这两天看了 Event Loop 相关的技术文章,写了一个测试代码,发现测试结果并不稳定,可能会因为一些变量而导致不同的结果。不知道是浏览器实现的问题还是测试代码的问题。

参考技术文章

为了后面方便引用,给参考文章加上编号
JavaScript 运行机制详解:再谈Event Loop - $RYF
并发模型与Event Loop - MDN - $MDN

首先贴代码:





  Event Loop Test
  
  



  

  


测试环境

Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3041.0 Safari/537.36

运行了很多次,把比较典型的三种结果贴出来:

测试结果一: XHR加载jQuery用时503ms
setTimeout方法调用完成: 1489654050312
javascript-temp.html:28 XHR请求发出: 1489654050833
javascript-temp.html:37 同步任务延迟完成时间:: 2043.388916015625ms
javascript-temp.html:38 第一个
            
            
            
            
            
            
            
            
            
            
            
            

            
ringa_lee
ringa_lee

ringa_lee

全部回复(5)
怪我咯

说下我的理解吧,先明确几个基本概念

  1. js引擎只负责实现ecmascript标准,按照标准执行代码,它不关心也不知道event loop的存在,你给它什么代码它就执行什么

  2. event loopjs运行环境内部使用的机制,运行环境也就是Node以及各种各样的浏览器

  3. js引擎执行代码时会有一个执行栈,执行栈中的代码执行完毕后,浏览器(JS运行环境)才会从event queue中取出事件,如果该事件有对应的callback,则再次交给js引擎执行。

  4. 所有的事件在产生后都会被浏览器放到event queue中,事件可以来源于鼠标键盘、网络IO、定时器等。event queue一般是由多个不同优先级的队列组成,分别对应不同类型的事件,具体的细节由实现者自己决定。

现在,根据你描述的问题,我尝试还原一下整个过程。

  1. 浏览器取得HTML文件后,做的第一件事就是解析HTML(它内部有另外的模块来做这件事),构建DOMTree,当遇到script标签,会停止解析,交给js引擎执行标签内的javascript代码块,这就是我们通常说的js会阻塞页面渲染。 此时js引擎执行栈中只有第一个script标签内的代码。一旦这段代码执行完毕,浏览器会检查event queue,这个时候,根据event queue的情况以及浏览器自身的实现策略就可能会有不同的结果。

  2. 浏览器可能会按照先后顺序,或者它预定的优先级依次取出全部事件给js引擎执行callback,还有可能为了加快页面渲染速度,只取出部分高优先级事件。

  3. 最后浏览器继续解析页面,遇到下一个script标签再次交给js引擎执行代码

我在Chrome57下多次执行了你的代码,只会出现第1,3种情况

这很好理解,当第一段script执行完时,chrome取出事件时,忽略低优先级的timeout事件,如果已经有xhr事件则取出,没有就继续解析HTML,碰到第二个script再次用js引擎执行。

最后,已上纯属个人见解。有些概念其实依赖于具体的实现的,不同浏览器的差异可能会导致表现出来的行为就不一样,要深入的了解细节只能去看它们的源码了。。。

黄舟

先贴个有相关性的问题链接
1.第一个问题还是比较好理解的,很可能是在第二个script还未被解析(先解析后执行)的时候,第一个script中的异步回调被触发了。此时执行栈是空的。
2.前一半同问题一,后一半,异步回调的执行顺序跟建立的顺序无关,谁先触发,谁就在前面。
3.我觉得是对的。
4.等别的回答。
也得纠正上面的错误。

巴扎黑

这是一个蛮有意思的问题,但追根到底其实就是一个事件驱动的问题。

对于浏览器而言,setTimeout 消息和 XMLHttpRequest AJAX消息会被当作两种不同的消息放在消息队列里面。

消息,就会有众多因素而倒置输出的顺序不可预见!

既然如此,那么如果我们把两个消息拿掉,那么你的三个执行顺序全是正常的,这样第一个问题就迎刃而解。

以下来自MDN的一张Event Loop的图式:

其中 Queue 由浏览器维护,换句话说队列中的消息是由浏览器来放入的。

但这里有一个前置条件,就是不管是哪种消息,都遵循一种CPU TICK的时间限定,大概就是4ms(如果使用的是电池且量底的情况下可能会更长)。

因此,不知道题主有没有注意到一个细节,即两个消息的时间不可能会存在相隔1ms的情况,即:

javascript-temp.html:17 setTimeout延迟完成,执行回调: 1489664225856
javascript-temp.html:24 XHR请求完成,执行回调: 1489664225861
// 二者的时间不会存在 1489664225856 和 1489664225857

而对于Event Loop中的消息处理是不存在这种因素:

javascript-temp.html:25 XHR请求完成,执行回调: 1489654052881
javascript-temp.html:42 另外一个<script>标签: 1489654052882
// 当一个消息放进后,主线程的立即开始

不管是什么的消息,都是依赖于浏览器的事件驱动,那么这一话题就会伸到不同浏览器之间的差异。假如你把测试代码放在 Chrome、IE、Safari 等浏览器下,所执行的结果也和 Firefox 不同。更甚至,可能你把 <script><body> 迁移到 <head> 里面,也可能会引发不同的结果。

说了,那么多,我也没办法提供一个准确的答案!

伊谢尔伦

照理说 不应该出现结果2 的情况才对

PHP中文网

题主你理解这个eventloop机制了没?我最近也看到关于eventloop的资料,有点不明白事件队列里面有没有优先级之分,或者说浏览器在将异步任务push进任务队列有没有优先级的,想ajax、dom事件、setTimeout他们有没有什么先后顺序

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号