事件轮询

180 阅读3分钟

javascript 中,任务分为两种:

  1. 宏任务:包括整体代码script,setTimeout,setInterval, I/O,UI Rendering
  2. 微任务:Promise.then(非new Promise),process.nextTick(node中)

事件执行顺序:

先执行宏任务,再执行微任务。任务又分,同步任务,异步任务。同步任务进入主线程,异步任务进入Event Table并注册函数,异步事件完成后,会将回调函数放入Event Quene中(宏任务和微任务是不用的Event Quene)。同步任务完成后,会从Event Quene中读取事件放入主线程执行,回调函数中可能还函数有不同的任务,会循环执行上述操作的。

例子1:

// a
console.log('script start');

// b
setTimeout(function() {// e
  		console.log('setTimeout');
}, 0);

// c
Promise.resolve().then(function() { // f 
  console.log('promise1');
}).then(function() {// g
  console.log('promise2');
});

//d
console.log('script end');

// script start
// script end
// promisel1
// promisel2
// setTimeOut

第一次轮询,刚开始微任务为空

执行宏任务:执行a,b,将e添加到Event Table,执行c,将f,g添加到微任务,执行d,

打印结果:a,d的值(script start, script end)

执行微任务:里面有f,g,执行f,g

打印结果:f,g的值(promise1, promise2)

第二次轮询:

执行宏任务:执行e

打印结果:e (setTimeout)

执行微任务:空

例子2:

setTimeout(function() {// a
    console.log('setTimeout');
},1000)

new Promise(function(resolve) {// b
    console.log('promise');
    resolve();
}).then(function() {// c
    console.log('then');
})
// d
console.log('console');

// promise
// console
// then
// setTimeout

第一次轮询:

执行宏任务:将a放到Event Table注册,执行同步b,添加c到微任务,执行d

打印结果:b,d的值(promise, console)

执行微任务:执行c

打印结果:c(then)

第二次轮询:

执行宏任务:a已经执行完毕,提取到主线程

打印结果:a(setTimeout)

执行微任务:空

例子3:

// a
console.log('script start')

async function async1() { // b
  // c
  await async2()
  // d
  console.log('async1 end')
}
async function async2() {// e
  console.log('async2 end') 
}
async1()

setTimeout(function() {// f
  console.log('setTimeout')
}, 0)

new Promise(resolve => {// g
  console.log('Promise')
  resolve()
})
  .then(function() {// h
    console.log('promise1')
  })
  .then(function() {// i
    console.log('promise2')
  })

// j
console.log('script end')

// script start
// async2 end
// Promise
// script end
// promise1
// promise2
// async1 end
// setTimeout

// 作者:光光同学
// 链接:https://juejin.cn/post/6844903764202094606
// 来源:掘金
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

async/await 在底层转换成了 promisethen 回调函数。 也就是说,这是 promise 的语法糖。 每次我们使用 await, 解释器都创建一个 promise 对象,然后把剩下的 async 函数中的操作放到 then 回调函数中。每个 await,会新产生一个promise,但这个过程本身是异步的,所以该await后面不会立即调用。

第一次轮询:

执行宏任务:执行a,b,c,e,将f放到Event Table,执行g,将h,i放入微任务,执行j

打印结果:a,e,g,j(script start,async2 end,Promise,script end)

执行微任务:执行h,i,然后回到await位置返回的Promise的resolve函数,将其对待微任务队列中,执行

打印结果:h,i,d(promise1,promise2,async1 end)

第二次轮询

执行宏任务:执行f

打印结果:f(setTimeout)

总结一下事件的流程

执行一个 宏任务(栈中没有就从 事件队列中获取)

执行过程中如果遇到 微任务,就将它添加到 微任务的任务队列中

宏任务执行完毕后,立即执行当前 微任务队列中的所有 微任务(依次执行)

当前 宏任务执行完毕,开始检查渲染,然后 GUI线程接管渲染

渲染完毕后, JS线程继续接管,开始下一个 宏任务(从事件队列中获取)

参考文章:

「前端进阶」从多线程角度来看 Event Loop