浅谈JS的事件循环
我们需要补充一些前置知识,比如单线程模型是什么?
js是一个单线程的脚本语言,之所以为什么不是多线程而是单线程,是因为历史遗留的原因,脚本语言如果使用了多线程,那么一个线程操作了dom,第二个线程也操作了dom,那么浏览器改听谁的,如果是多线程会对开发者来说是一个弊大于利的事情;
那么没有了多线程就说明了,我们的任务需要在一个线程中进行,但是js虽然是单线程,但是还有很多线程,只是同一时间执行事件的线程只有一个,这个线程叫做主线程;
但是我们会发现,如果现在是单线程,执行任务要等到上一个任务执行结束才会到下一个,这对于一些IO操作,ajax请求操作是非常坑的事情,我们需要等到这些任务执行结束才会执行下面的,等待IO/ajax返回并不需要消耗CPU,还需要花时间等它们,非常不划算,所以js引入了新的概念叫做消息队列(任务队列)
js在执行任务的时候,会把所有的同步任务优先执行,把所有的异步任务挂起到其他队列,等到我们的同步任务全部清空,再看异步任务是否满足条件再添加到主线程中的任务队列中执行,比较经典的例子就是setTimeout;
let timer = setTimeout(() => {
console.log("这并不是准时的延迟噢")
}, 1000)
延时器的意思就是我需要等待一段时间把这样的任务放到主线程的最后面,那么这个参数传递的是1000,并不是真的1秒延迟执行,而是在主线程前面的任务执行完毕,如果前面有很多耗时的任务,那么这个1000指的就是最少时间而不是最终时间;
当我们的同步任务被主线程全部执行完毕,会去检查其他的队列中存在异步任务,如果检查出来异步任务满足条件那么就放到了主线程去执行,那么这个异步任务也就变成了同步任务,然后当主线程又一次清空了,又要去找异步任务去执行,如果一旦任务队列是空的,那么程序执行结束。每一个消息会与一个函数进行一个关联,等到执行到此任务(消息)的时候,会执行对应的函数,如果没有这个函数,那么这个消息就会遗失;
那么我们把这样的一次一次的去查询异步任务是否满足条件进入主线程执行任务的这个过程称之为事件循环机制
维基百科对事件循环机制定义是这样的
“Event Loop是一个程序结构,用于等待和发送消息和事件(a programming construct that waits for and dispatches events or messages in a program)”。可以就把Event Loop理解成动态更新的消 息队列本身。
可以用代码这样表示
while(queue.waitForMessage()){
queue.processNextMessage();
}
那么任务可以分为2类:
- 宏观任务 (同步) :
没有被引擎挂起,在主线程中执行的任务,只有前面的任务执行完毕才能轮到下一个 - 微观任务 (异步)
被js引擎挂起的任务,这些任务会在任务队列中,只有js引擎认为有条件进入主线程,那么就加入到主线程摇身一变就变成了同步任务,排在异步任务后面的代码,不用等前面的执行,会立即执行;
关于单线程问题,除了主线程,还有ajax线程,setTimeout线程,事件监听线程,这也都证明了js是单线程处理事件的语言,而不是只有一个线程;