浅谈JavaScript 事件循环(Event loop)

416 阅读3分钟

这是我参与新手入门的第1篇文章

src=http___dingyue.ws.126.net_2020_0520_5b0db6afj00qaloi40023c000hs00g4c.jpg&refer=http___dingyue.ws.126.jpg

事件循环概念

JavaScript 事件循环(Event loop)是JavaScript的执行机制。 分为浏览器事件循环机制和 Node 事件循环机制。 本篇文章浅谈的为浏览器事件循环机制。

事件循环的作用

在了解事件循环之前我们先从JavaScript本身说起。 JavaScript是一门单线程语言。

单线程

单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。

看到这有的小伙伴们就要问了,那是不是如果有个程序执行时,前面的任务太慢了,那后面的任务也要一直等着???直到前面任务执行完毕后才开始下一个???

这就需要JavaScript 事件循环机制了。

事件循环的实现

1625556200(1).png

如图所示,JavaScript 单线程中的任务进入执行栈时会判断为同步任务和异步任务。 如果为同步任务会在按照顺序排队放入主线程执行,如果为异步任务则进入Event Table并注册函数,当指定的事情完成时,Event Table会将这个函数移入Event Queue。当主线程内的任务执行完毕为空时,会去Event Queue读取对应的函数,进入主线程执行。这个过程不断循环,称之为事件循环

同步和异步

同步:同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。

异步:异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。js常见的异步任务主要有:

网络请求(Ajax)

事件触发(onclick | onchange etc.)

定时函数(setTimeout | setInterval)

回调函数

宏任务(macro-task)、微任务(micro-task)

除了广义的同步任务和异步任务,JavaScript 单线程中的任务可以细分为宏任务和微任务。

macro-task包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。

micro-task包括:process.nextTick, Promises, Object.observe, MutationObserver。

第一次事件循环中,JavaScript 引擎会把整个 script 代码当成一个宏任务执行,执行完成之后,再检测本次循环中是否寻在微任务,存在的话就依次从微任务的任务队列中读取执行完所有的微任务,再读取宏任务的任务队列中的任务执行,再执行所有的微任务,如此循环。JS 的执行顺序就是每次事件循环中的宏任务-微任务。

举个栗子

console.log(1);
setTimeout(function() {
    console.log(2);
})
var promise = new Promise(function(resolve, reject) {
    console.log(3);
    resolve();
})
promise.then(function() {
    console.log(4);
})
console.log(5);

第一次事件循环,整段代码作为宏任务进入主线程执行。

遇到了 setTimeout ,到过了指定的时间后将回调函数放入到宏任务的任务队列中。

遇到 Promise,将 then 函数放入到微任务的任务队列中。

整个事件循环完成之后,检测微任务的任务队列中是否存在任务,存在就执行。 第一次的循环结果打印为: 1,3,5,4。

再到宏任务的任务队列中按顺序取出一个宏任务到栈中让主线程执行,那么在这次循环中的宏任务就是 setTimeout 注册的回调函数,执行完这个回调函数,发现在这次循环中并不存在微任务,就准备进行下一次事件循环。

宏任务队列中已经没有了要执行的任务,所以结束事件循环。

结果是 1,3,5,4,2