夯基础- js event loop机制

js运行机制

event loop事件循环

  1. js分为同步任务和异步任务,所有的同步任务都在主线程上执行
  2. 另外存在着一个“任务队列”,只要异步的任务有了结果,便在任务队列里面加入一个事件
  3. 当主线程的的同步任务都执行完了,执行栈清空,这个时候会去读取任务队列,依次把他们扔到主线程执行
  4. 这个过程不断循环,就成了js的事件循环机制。

所以我们不难理解有的时候setTimeout(fn, 0)没有立即执行,它只是被立即加入到任务队列了,可能那个时候主线程还没有执行完毕,所以它要等着,等js引擎空闲的时候再执行。

任务队列是由js的事件触发线程控制,不是js引擎所控制,可能js引擎太忙了,浏览器又单独开了一个线程

宏任务(macrotask) 、微任务(microtask)

如果我们遇到诸如类似的,那么谁先执行呢,这就引出了宏任务,微任务的概念。

console.log(1)

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

setTimeout(() => {
    console.log('settimeout')  
})
宏任务

每次执行栈执行的代码就是一个宏任务,包括把任务队列的事件加入到主线程的执行,一个宏任务的执行相当于一个task,浏览器会在一个task结束之后对页面进行重新渲染。

常见的macrotask:setTimeout、setInterval、setImmediate、I/O等

微任务

microtask又是个什么东东?在一个task执行之后紧跟着执行的东西,也就是一个task执行完毕,接下来会把它在执行期间产生等所有微任务都进行执行,也就是要把微任务队列执行清空掉,接下来浏览器开始对页面进行重新渲染。

常见的microtask:Promise.then、process.nextTick、MutaionObserver

夯基础- js event loop机制_第1张图片

小结

结合上宏任务、微任务再来总结一下js的事件循环机制。

  1. 执行同步代码,也就是开始一个宏任务的执行
  2. 如果遇到异步,等异步任务有了运行结果后再把他们放到事件队列,如果属于微任务的话加入微任务队列。
  3. 宏任务执行完毕,接下来把这个task执行过程中产生的微任务依次执行
  4. 微任务全部执行完毕,浏览器开始重新渲染
  5. 去事件队列读取下一个宏任务,不断循环上面过程。
// 举个栗子?

console.log('1');

setTimeout(function() {
    console.log('2');
    Promise.resolve().then(function() {
        console.log('3');
    })
    new Promise(function(resolve) {
        console.log('4');
        resolve();
    }).then(function() {
        console.log('5')
    })
}, 100)

Promise.resolve().then(function() {
    console.log('6');
})

new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})

setTimeout(function() {
    console.log('9');
   Promise.resolve().then(function() {
        console.log('10');
    })
    new Promise(function(resolve) {
        console.log('11');
        resolve();
    }).then(function() {
        console.log('12')
    })
})

console.log('13')

第一次事件循环,首先打印了1,然后遇到setTimeout,我们记为setA,setA去外面排队,接下来Promise.then属于微任务,加入微任务队列。再往下遇到new Promise,直接执行打印7,then函数加入微任务队列。再继续又遇到setTimeout,我们记为setB,排队等着。然后打印13。所以第一轮事件循环结束,我们直接打印了1,7,13,紧接着依次打印微任务队列里面的6,8。

现在第一轮事件循环结束,还有setA,setB等待执行,先执行哪个呢?答案是setB。虽然定时器线程先捕捉到了setA,但是setA延迟时间是100ms,而setB是立即执行的,setB会在setA之前被加入到事件队列。所以当第一轮事件循环结束,此时会把setB从事件队列拉到执行栈中执行,第二轮事件循环开始了。

第二次循环首先打印了9,然后遇到微任务,把10加入到微任务队列,往下又遇到了new Promise直接执行,打印11,then函数的12被加入到微任务,本次事件循环结束。依次打印了9,11,10,12.

第三轮事件循环开始,setA所在的宏任务,首先打印了2,然后3被加入到微任务,同样的,打印4,then函数的5被加入到微任务队列。本轮事件循环结束,依次打印2,4,3,5.

最终结果:1,7,13,6,8,9,11,10,12,2,4,3,5

参考资料: https://dailc.github.io

你可能感兴趣的:(前端,javascript)