宏任务与微任务
1.同步任务和异步任务
JavaScript是单线程执行的语言,在同一个时间只能做一件事情。这就导致后面的任务需要等到前面的任务完成才能执行,如果前面的任务很耗时就会造成后面的任务一直等待。为了解决这个问题JS中出现了同步任务和异步任务。
(1)同步任务
在主线程上排队执行的任务只有前一个任务执行完毕,才能执行后一个任务,形成一个执行栈。
(2)异步任务
不进入主线程,而是进入任务队列,当主线程中的任务执行完毕,就从任务队列中取出任务放进主线程中来进行执行。由于主线程不断重复的获得任务、执行任务、再获取再执行,所以者种机制被叫做事件循环(Event Loop)
2.宏任务与微任务
我们会发现异步任务种的事件并不是严格按照顺序来执行的,如下:
<script>
setTimeout(() => {
console.log("定时器");
}, 0);
Promise.resolve().then(value=>{
console.log('Promise');
})
console.log('console');
</script>
执行结果为:
这时我们就会发现任务队列中的任务是有优先级的,上文中的代码Promise的优先级高于setTimeout。再往下探索发现,任务队列中的任务也是分为两种,宏任务和微任务。
常见的微任务有Promise
、process.nextTick
、MutationObserver
常见的宏任务:script
、setTimeout
、setInterval
、setImmediate
发出疑问:到底是宏任务优先还是微任务优先?(个人观点:微任务优于宏任务)
经过实践并参考了前辈们的文章后做出总结:
Event Loop执行顺序为:
先执行宏任务script
,并执行里面的同步任务;执行栈为空后查询是否存在微任务,存在就立即执行,然后开始下一轮的事件循环。
从上面的执行顺序可知,如果宏任务中有大量的计算并且需要操作DOM的话,为了更快响应,可以把操作DOM放进微任务务中。
3.定时器
定时器在开发中经常能用到,一个是超时调用定时器setTimeout(),也叫做爆炸定时器,另一个是间歇调用定时器setInterval()。但是在我们设置延时后会发现延时的时间和触发回调函数的时间不一样,究其原因,仍然是和执行机制有关。
首先我们知道定时器是宏任务,在定时器计时完毕后会将它的回调函数加入到任务队列中,等待下一次的事件循环。这也就是说下一次执行的定时器仅仅只是回调函数,计时已经在定时器模块中完成了。
如下代码:
setInterval(()=>{
console.log('计时5S');
},5000)
for(let i=0;i<10009;i++){
console.log('');
}
毫无疑问,首先执行的是for循环,但是当for循环执行结束后会立即打印出“计时5S”两次。
4.Promise代表的微任务
代码:
setTimeout(()=>{
console.log('定时器');//1
},0)
new Promise(resolve=>{
console.log('Promise');//2
resolve()
})
.then(()=>{console.log('then')});//3
console.log('console');//4
//输出结果:2>4>3>1
5.任务共享内存
let i=0
setTimeout(()=>{
console.log(++i);//1
},0)
setTimeout(()=>{
console.log(++i);//2
},0)
//把两个定时器加入到任务队列中,执行完第一个时i自增变为1,再执行第二个定时器函数后i为2