前言
最近在看JavaScript的EventLoop机制,理解这个机制,能够提高我们对JavaScript中异步处理和代码运行的认知和理解。
查阅资料发现,JavaScript的eventloop机制在浏览器环境和nodejs环境是不同的,这篇文章是以浏览器环境为前提的。
首先分区:
- 控制台就是我们常用的测试输出的区域,这里用来显示测试输出。
- 代码段是我们将要执行的代码,并无区分,只是字符串。
- 执行栈就是当前正在执行的代码区域,可以说这是JavaScript单线程的主线程执行,所有的执行结果会在这里产生。
- WebApis是执行栈调用的各种Apis,比如DOM事件(点击,滑动,键盘等)、定时器事件(setTimeout,setInterval)、AJAX等,这些api可以在任务队列中添加各种事件
- 任务队列是存放由WebApis置入的事件函数,等待Eventloop机制调用
- EventLoop是一个轮询机制,当执行栈中的代码执行完毕之后,会取得任务队列队首的事件函数置入执行栈。
分析样例
我们分析一个这一个段代码的执行过程,和几个分区的变换情况。
1.首先,将目前的代码段置入执行栈
2.执行第一行console.log('你好,我是杰哥'); 执行结果就是在控制台打印字符串:你好,我是杰哥,并从执行栈删除这行代码。
3.执行执行栈中 let set_timeout = setTimeout() 这段代码。这段代码将一个函数放到WebApis并在执行栈清空3s后将函数置入任务队列。
这里解释一下为什么我们把setTimeout()定时器函数调用结果赋值给一个变量。
首先要知道setTimeout是一个函数, 在函数后面加一个括号叫做函数调用表达式,就代表调这个函数。
而调用setTimeout这个函数的结果就是若干时间后执行代码。
你把setTimeut()赋值给另一个变量,只是把函数调用的结果(即返回值)给了那个量,定时器函数的返回值是一个整数。返回的是一个ID号。从1开始。
clearTimeout(set_timeout),会清除掉这个定时函数,但是不会改变id值。在这里,set_timeout = 1; set_interval = 2;
4.执行执行栈中 let set_timeout = setInterval() 这段代码。这段代码将一个函数放到WebApis中,并在执行栈清空后每一秒将此函数置入任务队列中一次。
5.执行执行栈中的console.log('你好,我是灼子');执行结果就是在控制台打印字符串:你好,我是灼子,并从执行栈删除这行代码。
6.至此,执行栈中的代码清空,然后EventLoop开始循环遍历任务队列中的事件函数,但是此时任务队列中并没有任务可执行。同时在执行栈中代码清空后,webApis开始运转(时间开始流转)在第1s后,setInterval定时器到时间,将函数function(){console.log('setInterval回调函数执行');}添加至任务队列。
7.添加至任务队列之后,由于执行栈中并无执行代码,所以eventLoop一直在循环查看,发现任务队列中添加了新的任务函数后,立刻将此任务函数置入执行栈,并执行此代码(注意在执行过程中eventLoop是不会继续循环查看的,但是WebApis会继续读秒,因为无论是eventLoop还是执行栈中执行代码,速度都很快,所以WebApis的读秒不会明显的表现出来)。
8.执行结束后删除执行栈中执行过的代码,删除后执行栈清空,此时EventLoop继续循环查看任务队列,WebApis仍然读秒,到2s的时候,setInterval定时器会再次将一个函数function(){console.log('setInterval回调函数执行');}添加至任务队列。
9.由于执行栈为空,此时EventLoop一直在循环查看任务队列,在添加任务后,Eventloop会直接将任务队列中的代码置入执行栈,并执行,执行结束后清空执行栈。
10.WebApis仍然在读秒,到第三秒时,setTimeout定时器和setInterval到时,这是会根据ID(即添加定时器先后顺序)执行将事件函数加入任务队列,setTimeout定时器先添加。添加后setTimeout定时器注销。EventLoop一直在循环查看任务队列
注意,因为EventLoop一直在循环查看任务队列,当setTimeout定时器添加事件函数后,会立即置入执行栈并执行。这里我不确实是否会将setInterval定时器到时的事件函数置入任务队列,不过最后的结果是没有执行setInterval定时器的任务。我猜测是因为执行栈中的代码执行速度很快,注销setInterval定时器的时候,还未添加到任务队列。
10.EventLoop一直在循环查看任务队列,当setTimeout定时器添加事件函数后,会立即置入执行栈并执行。按行执行代码,首先会执行console.log('setTimeout回调函数执行');并在控制台打印。然后会执行window.clearInterval(set_interval)会将webApis中的setInterval定时器取消,最后执行console.log("setInterval定时器注销");并在控制台打印。
11.至此,执行栈、webApis、人任务队列都为空,代码执行完毕,分析的输出结果如下。我们在浏览器中测试一下。 执行结果如下,我们分析的结果与实际相符。
总结
本文是根据阮一峰笔记中的EventLoop相关内容,以及查阅一些文档总结分析的,如果有错误欢迎在评论区指正。