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

对event loop事件循环的理解 #2

Open
Jmingzi opened this issue Jan 2, 2017 · 2 comments
Open

对event loop事件循环的理解 #2

Jmingzi opened this issue Jan 2, 2017 · 2 comments

Comments

@Jmingzi
Copy link
Owner

Jmingzi commented Jan 2, 2017

对于这个概念,理解过多次才有点认知,本文理解尚浅,可查看后续文章加深

以前似乎并不知道有这个概念,(很害羞的说出这句话~),直到16年倒数第二天逛掘金时遇到这题:

setTimeout(function(){
	console.log(4)
}, 300)	
setTimeout(function(){
	console.log(3)
}, 200)	
for (var i=0; i<1000000; i++) {
	console.log(1)
}
setTimeout(function(){
	console.log(2)
}, 100)	

看到这题的时候,凭借“习惯”的想到,for循环的时间会不会很长,超过100ms,那这答案不确定了。后面再想到js是单线程从上到下执行,那很自然想到结果是1 2 4 3

后面又看到一题,直接懵逼

(function test() {
    setTimeout(function() {console.log(4)}, 0);
    new Promise(function executor(resolve) {
        console.log(1);
        for( var i=0 ; i<10000 ; i++ ) {
            i == 9999 && resolve();
        }
        console.log(2);
    }).then(function() {
        console.log(5);
    });
    console.log(3);
})()

看了题就发现其实知识还是无穷无尽的。好吧,废话说了这么多,进入正题~(以下内容仅属个人看法,有误之处希望批评指正)

基本概念

js 只有一个main thread 主进程和call-stack(一个调用堆栈),所以在对一个调用堆栈中的task处理的时候,其他的都要等着。task等待被加入调用堆栈等待执行的时候,被放在task queue事件队列之中。
每当主线程完成一个任务的时候,他就会去调用堆栈中获取task执行。

而task分为:

  • macrotask:也称为task,包含了script(整体代码),setTimeout, setInterval, setImmediate, I/O, UI rendering
  • microtask:process.nextTick, Promises, Object.observe, MutationObserver

二者的关系:

JavaScript引擎首先从macrotask queue中取出第一个任务,执行完毕后,将microtask queue中的所有任务取出,按顺序全部执行;
然后再从macrotask queue中取下一个,执行完毕后,再次将microtask queue中的全部取出;
循环往复,直到两个queue中的任务都取完。

【参考】Macrotask Queue和Microtask Quque

题目解答

有了这些概念以后再来看上面第一题。

  • 1 setTimeout 入栈,300ms后打印4
  • 2 setTimeout 入栈,200ms后打印3
  • 3 for循环,打印1
  • 4 setTimout 要等待for循环完成 再入栈,100ms后打印2

所以结果是1 3 4 2,关于2的打印顺序与for循环的时间有关,我们这里的for时间大于400ms,所以2才最后打印。

第二题是一个立即执行函数:

  • 1 setTimeout入栈
  • 2 声明函数executor,打印1,new Promise(executor)
  • 3 for循环
  • 4 等待for循环执行完毕,打印2,然后打印3
  • 5 for执行完后,调用Promise.resolve()
  • 6 根据microtask 和 task机制,所以先打印5,最后打印4

所以结果是1 2 3 5 4

【参考】从Promise来看JavaScript中的Event Loop、Tasks和Microtasks

最后再来一道题加深理解:

console.log('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')
    })

题解:
我们将macrotask 和 microtask 看作是2个队列,不断的清空入栈执行。

  1. setInterval和setTimeout 1被加入到task,promise 1和promise 2被加入到microtask
  2. 清空microtask,打印promise 1和promise 2,执行task队列,打印setInterval和setTimeout 1
  3. setInterval被加入到task,promise 3和promise 4被加入到microtask,setTimeout被加入到task
  4. 清空microtask,打印 promise 3和promise 4,执行task队列,打印setInterval,setTimeout 2
  5. setInterval被加入到task,promise 5和promise 6被加入到microtask
  6. 清空microtask,打印 promise 5和promise 6,clearInterval

所以打印顺序是:start, promise 1, promise 2, setInterval, setTimeout 1, promise 3, promise 4, setInterval, setTimeout 2, promise 5, promise 6

在一个事件循环的周期(cycle)中一个 (macro)task 应该从 macrotask 队列开始执行。当这个 macrotask 结束后,所有的 microtasks 将在同一个 cycle 中执行。在 microtasks 执行时还可以加入更多的 microtask,然后一个一个的执行,直到 microtask 队列清空。

【参考】
理解事件循环一(浅析)
理解事件循环二(macrotask和microtask)
Javascript 单线程

@Jmingzi Jmingzi changed the title event loop事件循环 对event loop事件循环的理解 Jan 2, 2017
@pengVc
Copy link

pengVc commented Jul 24, 2019

Firefox 、Safari 是可以这样讲通
较个真, Chrome 里面答案真的不完全是这样.

image

@Jmingzi
Copy link
Owner Author

Jmingzi commented Jul 26, 2019

@pengVc 感谢提醒,刚执行多次发现确实偶尔会出现这种情况,performance分析,莫名其妙会执行2遍,chrome bug?

WeChatdee91b5ec24d70437cf1843da75110cb

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

2 participants