首先声明,本人是从事web前端行业,然后这次讨论是在浏览器中执行的~
现在进入主题,Event Loop这个词,最早在2013年看阮老师的文章有看到,后面再次接触,是阮老师再篇了一文,解释前一篇的理解错误。
以上是过往,然后再次接触到这个词,是在近几年,因为面试中被问到了让我解释下事件循环,一开始我还以为是捕获事件流之类的,但对方说出了Event Loop,然后有点印象了,接着回答的很潦草接着,就没有接着了
面试回来之后,自然是再次搜索理解
然后,接触到了一个比阮老师Event Loop更详尽版本的解释,因为这个版本还划分了宏任务和微任务;
以下是这个版本的执行逻辑:
- 从宏任务的头部取出一个任务执行;
- 执行过程中若遇到微任务则将其添加到微任务的队列中;
- 宏任务执行完毕后,微任务的队列中是否存在任务,若存在,则挨个儿出去执行,直到执行完毕;
- GUI 渲染;
- 回到步骤 1,直到宏任务执行完毕;
然后相应的,给出了,哪些是宏任务,哪些是微任务,而且宏任务和微任务都是以先进先出,队列的方式执行的;
宏任务如下:
script、setTimeout、setInterval、I/O、UI 交互事件、postMessage、
微任务如下:
Promise.then
以上,我把有关node中的API去掉了。
然后,我的疑惑就来了,主要疑惑就是宏任务中的script具体指的是哪块!
所以,我先假设那个script是指页面中的所有javascript代码,于是有如下结构代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>demo</title>
</head>
<body>
<script src="./script1.js"></script>
<script src="./script2.js"></script>
<script>
console.log('外-111')
setTimeout(() => {
console.log('外-setTimeout-111')
}, 0);
</script>
<script>
console.log('外-222')
setTimeout(() => {
console.log('外-setTimeout-222')
}, 0);
setTimeout(() => {
console.log('外-setTimeout-333')
}, 0);
new Promise(resolve => {
console.log('外-Promise1')
resolve()
})
.then(function () {
console.log('外-promise2')
})
.then(function () {
console.log('外-promise3')
})
</script>
</body>
</html>
链接的script1.js代码
console.log("内-111");
setTimeout(() => {
console.log("setTimeout-内-111");
}, 0);
链接的script2.js代码
console.log("内-222");
setTimeout(() => {
console.log("setTimeout-内-222");
}, 0);
setTimeout(() => {
console.log("setTimeout-内-333");
}, 0);
new Promise((resolve) => {
console.log("内-Promise1");
resolve();
})
.then(function () {
console.log("内-promise2");
})
.then(function () {
console.log("内-promise3");
});
如果我理解上面的理论无误的话,执行逻辑应该是这样,
- 先整个提取所有代码;
- 然后按先后顺序,执行同步任务;
- 同步任务完毕,执行微任务,如Promise.then代码;
- 最后执行完宏任务代码块,如setTimeout;
然后输出的顺序应该是这样如下:
内-111
内-222
外-111
外-222
内-Promise1
内-promise2
内-promise3
外-Promise1
外-promise2
外-promise3
setTimeout-内-111
setTimeout-内-222
setTimeout-内-333
外-setTimeout-111
外-setTimeout-222
外-setTimeout-333
但真实的执行顺序是这样的(Chrome119.0.6045.200(正式版本) (64 位)):
内-111
setTimeout-内-111
内-222
内-Promise1
内-promise2
内-promise3
外-111
外-222
外-Promise1
外-promise2
外-promise3
setTimeout-内-222
setTimeout-内-333
外-setTimeout-111
外-setTimeout-222
外-setTimeout-333
按照真实执行来看,就得出对script理解,不应该为整体代码。
然后继续假设,如果按每一个script代码是一个宏任务去执行,那如上示例应该是4块宏任务区往复的执行,理论上执行顺序就为:
内-111
setTimeout-内-111
内-222
内-Promise1
内-promise2
内-promise3
setTimeout-内-222
setTimeout-内-333
外-111
外-setTimeout-111
外-222
外-Promise1
外-promise2
外-promise3
外-setTimeout-222
外-setTimeout-333
显然一对比,也不对。再次假设,如果按照链接里的js代码合并是一个宏任务,写在页面中的代码为一个宏任务,那再次按照执行逻辑就应该是这样的顺序:
内-111
内-222
内-Promise1
内-promise2
内-promise3
setTimeout-内-111
setTimeout-内-222
setTimeout-内-333
外-111
外-222
外-Promise1
外-promise2
外-promise3
外-setTimeout-111
外-setTimeout-222
外-setTimeout-333
很显然,还是没有对上。
然后我就想,是否Chrome为老不准,走出自己的style,没有按标准来呢。
然后我用我的Firefox(121.0.1 (64 位)执行了一下,如下:
内-111
内-222
内-Promise1
内-promise2
内-promise3
外-111
外-222
外-Promise1
外-promise2
外-promise3
setTimeout-内-111
setTimeout-内-222
setTimeout-内-333
外-setTimeout-111
外-setTimeout-222
外-setTimeout-333
而Microsoft Edge(119.0.2151.44)执行,与谷歌一致。
因为没有苹果系列的设备,所以请路过的大佬,帮忙执行下~
然后也想按照软老师的执行顺序逻辑去看看,但按我的理解,里面只有同、异步之分,如果按异步代码都是入队列,先进先执行,好像也不太符合实际情况~
如上,我就开始怀疑现通行版本对Event Loop的解释是否有误~
另外我自己去找权威书籍《JavaScript高级程序设计(第4版)》 和 《JavaScript权威指南(第7版)》,里面并无这些解释~
希望路过大佬有指出我论证的错误也希望大佬解答下Event Loop
如果再得空闲,我也会整理下自己查阅的理解,到时候再分享给大家~