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

JS eventLoop #9

Open
lou1swu opened this issue Jan 16, 2018 · 1 comment
Open

JS eventLoop #9

lou1swu opened this issue Jan 16, 2018 · 1 comment

Comments

@lou1swu
Copy link
Owner

lou1swu commented Jan 16, 2018

No description provided.

@lou1swu
Copy link
Owner Author

lou1swu commented Jan 16, 2018

一直以为浏览器的事件循环机制就是执行栈加任务队列,直到我看到一篇译文,才觉得事件循环机制博大精深,并顺便把nodeJS eventLoop也了解清楚,这对于我们后期编写复杂的异步逻辑很有帮助。

本文将尽可能深入地介绍浏览器中的JS eventLoop以及nodeJS eventLoop,先向参考的文章致敬。
本文链接:https://github.com/Lighting-Jack/Jack-blog/issues/9/

参考
1. http://www.ruanyifeng.com/blog/2014/10/event-loop.html - 阮一峰介绍事件循环
2. http://www.wemlion.com/post/nodejs-event-loop/ - [译] 理解 Node.js 事件循环
3. http://blog.csdn.net/yezhenxu1992/article/details/51731237 - 深入剖析nodeJS的异步io

JS eventLoop

单线程

js是单线程的,但浏览器内核是多线程,在内核控制下各线程相互配合以保持同步,一个浏览器通常由以下常驻线程组成:

  • GUI 渲染线程
  • JavaScript引擎线程(主线程)
  • 定时触发器线程
  • 事件触发线程
  • 异步http请求线程

需要注意的是主线程和GUI线程是互斥的,表现为耗时长的同步脚本是会使页面出现卡顿的;页面频繁的回流重绘也会导致交互出现延迟。

执行栈、任务队列、事件循环

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

简言之,执行栈就是执行同步任务的容器�,任务队列就是异步任务的回调函数存放的地方,事件循环就是主线程从任务队列中取任务,放置到执行栈中执行,并且不断地循环此过程。这么说可能有些枯燥,请看下图:
image

事件循环的全称应该叫做浏览器的事件循环,因为浏览器内部中存在一个消息循环池,也叫事件循环,本身v8引擎并不提供事件循环机制。浏览器的事件循环和nodeJS的事件循环是不一样的,关于nodeJS的事件循环将在下文阐述。�

没那么简单的任务队列

我一直认为任务队列只有一条,只要是异步任务的回调函数,就推入任务队列就是了,直到我了解了microTaskmacroTask

按照 WHATWG 规范,每一次事件循环(one cycle of the event loop),只处理一个 (macro)task。主线程执行完同步任务后会先检查 microtask queues并完成里面的所有任务后再执行 macrotask。处理这些 microtask 时,还可以将更多的 microtask 入队,它们会一一执行,直到整个 microtask 队列处理完。

microTask和macroTask各自涉及的操作如下:

  • microTask queues:process.nextTick, Promises, Object.observe(废弃), MutationObserver
  • macroTask queues(task queues):setTimeout, setInterval, setImmediate, I/O, UI rendering

这么说可能有些晦涩难懂,我们直接分析一串代码的实际执行结果:

console.log('script start')

const interval = setInterval(() => {  
  console.log('setInterval')
}, 0)

setTimeout(() => {  
  console.log('setTimeout 1')
  Promise.resolve().then(() => {
    console.log('promise 3')
  }).then(() => {
    console.log('promise 4')
  }).then(() => {
    setTimeout(() => {
      console.log('setTimeout 2')
      Promise.resolve().then(() => {
        console.log('promise 5')
      }).then(() => {
        console.log('promise 6')
      }).then(() => {
        clearInterval(interval)
      })
    }, 0)
  })
}, 0)

Promise.resolve().then(() => {  
  console.log('promise 1')
}).then(() => {
  console.log('promise 2')
})

// 在不同环境下,结果可能有差别
script start  
promise1  
promise2  
setInterval  
setTimeout1  
promise3  
promise4  
setInterval  
setTimeout2  
setInterval  
promise5  
promise6

伪图如下:(实际的图我稍后补上...)
image

解析如下:

  1. eventLoop ONE
  • MacroTask(Task):setInterval
  • MicroTask:promise.then、promise.then
  • Stack:console.log
  • Log:script start、promise1、promise2、setInterval
  1. eventLoop TWO
  • MacroTask(Task):setTimeout
  • MicroTask:null
  • Stack:null
  • Log:setTimeout 1
  1. eventLoop THREE
  • MacroTask(Task):setInterval
  • MicroTask:promise.then、promise.then、promise.then
  • Stack:console.log
  • Log:promise 3、promise 4、setInterval
  1. eventLoop FOUR
  • MacroTask(Task):setTimeout
  • MicroTask:promise.then、promise.then、promise.then
  • Stack:console.log
  • Log:setTimeout 2、promise 5、promise 6

按照如上分析,执行结果应该是:

script start  
promise1  
promise2  
setInterval  
setTimeout1  
promise3  
promise4  
setInterval  
setTimeout2  
promise5  
promise6

实际结果之所以与理想结果不符,是因为不同浏览器的事件循环机制是会由差别的,这个差别我还没有做深入分析,这一部分待续...

下面这篇文章�由一个谷歌工程师(jake)维护,文章带领我们可视化了事件循环的过程,方便我们深入理解;并且向我们解释了为什么同一串代码在不同浏览器中执行会产生不同的结果,很赞!
https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

下一篇:nodeJS eventLoop

@lou1swu lou1swu changed the title JS eventLoop & nodeJS eventLoop JS eventLoop Jan 16, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant