Skip to content

JavaScript之eventLoop #5

Open
Open
@chenyong9528

Description

@chenyong9528

事件循环(event loop)是什么?

JavaScript作为单线程脚本语言,同一时刻只能做一件事情,所有任务都需要排队,前一个任务结束,才会执行后一个任务。想一想,如果中间有一个耗时长的任务(异步任务),例如:ajax从服务器获取数据,难道我们要等待数据返回了才继续完成后面的任务吗?这显然不合理。

所以,事件循环就是帮我们解决这个问题的,它规定了如何来处理这些异步任务(ajaxsetTimeout等)。

事件循环的工作方式大概是这样:

进入程序,主线程从任务队列中取读第一个任务(script包裹的代码,我们可以认为它本身就是一个全局函数)加入执行栈(stack)执行,如果遇到异步任务并不会立即执行其中的代码,而是交给WebAPIs去处理,然后继续执行后面的代码,WebAPIs会在适当的时机将这些任务加入到任务队列(callback queue)中,执行栈中的代码执行完毕,就会取读任务队列中的任务,并加入执行栈执行,如此往复。

引用一张经典的图片:

event loop

以上图片中,异步任务有结果了就会将它所定义的回调函数加入到任务队列(callback queue)中,执行栈清空后就会取读任务队列。

看看以下代码:

console.log(1)

setTimeout(() => {
  console.log(2)
}, 1000)

console.log(3)

// 1
// 3
// 2(1s后)

以上代码中,当JavaScript引擎遇到setTimeout,会把它的回调函数和毫秒值交给WebAPIs,主线程继续执行后面的代码,所以先打印了13,1s之后WebAPIs中的回调函数会被加入到任务队列中,由于主线程现在是空闲状态,所以会立即取读任务队列并执行打印出2。

看看另一个例子:

console.log(1)

setTimeout(() => {
  console.log(2)
}, 0)

Promise.resolve().then( res => {
  console.log(3)
})

console.log(4)

// 1 4 3 2

以上代码中,setTimeoutPromise都是异步任务,但Promise先执行了,而不是setTimeout

原来在任务队列中还分为了:

微任务队列(Promise.then
以及宏任务队列(setTimeout

微任务属于本次任务的附属任务,本次任务结束后会取读微任务队里的所有任务并依次执行,然后开始下一个宏任务,也就是宏任务队列中的第一个任务,结束后继续查看微任务队列,能看出来事件循环大概是这样进行的:

宏任务 -> 微任务(全部) -> 宏任务 -> 微任务(全部) -> ···

所以,在上面的例子中,script本身就是一个宏任务,执行完毕就会取读微任务队列(Promise.then),然后是下一个宏任务(setTimeout)。换句话说,微任务是被添加到了本次任务的末尾,而宏任务添加到了下次事件循环的开头,本次任务产生的微任务永远在宏任务之前执行。

常见的宏任务(macrotask)和微任务(microtask):

宏任务:

  1. script整体代码
  2. setTimeout
  3. setInterval
  4. I/O 操作
  5. UI交互事件

微任务:

  1. Promise.then
  2. MutaionObserver

一次完整的事件循环

看看另一个例子:

const div = document.querySelector(".box")

div.style.backgroundColor = "red"

Promise.resolve().then( res => {
  div.style.backgroundColor = "blue"
})

以上代码中,通过观察发现,元素.box的背景色自始至终都是蓝色

const div = document.querySelector(".box")

div.style.backgroundColor = "red"

setTimeout(() => {
  div.style.backgroundColor = "blue"
}, 0)

以上代码中,元素.box的背景色会先变成红色,然后立马变成蓝色

两段代码反映一个问题,就是关于UI重新渲染的时机,是在一次事件循环的末尾进行的,也就是微任务之后会进行UI的重新渲染。

所以完整的事件循环应该是这样:

宏任务 -> 微任务(全部) -> UI渲染 -> 宏任务 -> 微任务(全部) -> UI渲染 ···

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions