Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript之eventLoop #5

Open
chenyong9528 opened this issue Jul 21, 2020 · 0 comments
Open

JavaScript之eventLoop #5

chenyong9528 opened this issue Jul 21, 2020 · 0 comments

Comments

@chenyong9528
Copy link
Owner

chenyong9528 commented Jul 21, 2020

事件循环(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渲染 ···

@chenyong9528 chenyong9528 changed the title JavaScript之eventLoop和执行上下文栈 JavaScript之eventLoop Jul 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant