我正在参加「掘金·启航计划」
浏览器 Eventloop 和 Node 中的有什么区别
众所周知 JS 是门非阻塞单线程语言,因为在最初 JS 就是为了和浏览器交互而诞生的。如果 JS 是门多线程的语言话,我们在多个线程中处理 DOM 就可能会发生问题(一个线程中新加节点,另一个线程中删除节点),当然可以引入读写锁解决这个问题。
JS在执行的过程中会产生执行环境,这些执行环境会被顺序的加入到执行栈中。如果遇到异步的代码,会被挂起并加入到Task(有多种task) 队列中。一旦执行栈为空,Event Loop就会从Task队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说JS中的异步还是同步行为
console.log('script start');setTimeout(function() { console.log('setTimeout');}, 0);console.log('script end');
- 以上代码虽然
setTimeout延时为0,其实还是异步。这是因为HTML5标准规定这个函数第二个参数不得小于4毫秒,不足会自动增加。所以setTimeout还是会在script end之后打印。 - 不同的任务源会被分配到不同的
Task队列中,任务源可以分为 微任务(microtask) 和 宏任务(macrotask)。在 ES6 规范中,microtask称为 jobs,macrotask称为task。
console.log('script start');setTimeout(function() { console.log('setTimeout');}, 0);new Promise((resolve) => { console.log('Promise') resolve()}).then(function() { console.log('promise1');}).then(function() { console.log('promise2');});console.log('script end');// script start => Promise => script end => promise1 => promise2 => setTimeout
- 以上代码虽然
setTimeout写在Promise之前,但是因为Promise属于微任务而setTimeout属于宏任务,所以会有以上的打印。 - 微任务包括
process.nextTick,promise,Object.observe,MutationObserver - 宏任务包括
script,setTimeout,setInterval,setImmediate,I/O,UI renderin
很多人有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了
script,浏览器会先执行一个宏任务,接下来有异步代码的话就先执行微任务
所以正确的一次 Event loop 顺序是这样的
- 执行同步代码,这属于宏任务
- 执行栈为空,查询是否有微任务需要执行
- 执行所有微任务
- 必要的话渲染
UI - 然后开始下一轮
Event loop,执行宏任务中的异步代码
通过上述的
Event loop顺序可知,如果宏任务中的异步代码有大量的计算并且需要操作DOM的话,为了更快的 界面响应,我们可以把操作DOM放入微任务中