因为js是单线程语言,所以我们需要搞懂js的执行机制。不要以为js是单线程语言,你以为的js就是这样的
let a = 'aaa';
console.log(a);
let b = 'bbb';
console.log(b);
然后实际上我们经常碰到这样的:
setTimeout(function() {
console.log("定时器开始了")
})
new Promise(function(resolve){
console.log("执行for循环")
for(var i = 0; i < 10000; i++) {
i == 99 && resolve()
}
}).then(function(){
console.log("执行then函数")
})
console.log("代码执行结束")
验证下你就想说,说好的一行一行执行呢?
所以有必要了解一下js的执行机制
一、关于js的执行机制
JS是一本单线程语言,HTML5提出了Web-Worker,模拟了多线程了,但是仍然改变了JS是单线程的本质。
二、js的事件循环
单线程很容易造成任务执行的阻塞,所以提出了任务了分类:
- 同步任务
- 异步任务
打开应用时,网页渲染过程就是一大堆同步任务,比如网页骨架、页面元素的渲染。然而一些图片耗时加载的任务就会是异步任务。
当指定的任务完成时,Event Table会将这个函数移入Event Queue。
主任务内的任务执行完毕为空会去Event Queue读取对应的函数,进入主线程。
上述过程不断重复,也就是Event Loop(事件循环)。
问:我们怎么知道主线程有没有为空呢?
js引擎中存在monitoring process进程。
三、setTimeout
威名远扬的setTimeout给人的印象就是异步、可以延迟执行
setTimeout(()=> {
console.log("延迟3秒执行")
}, 3000)
问题,为什么有时候设置三秒,有时候五六秒才执行呢?
因为3s后timeout完成,任务进入Event Queue,但是主线程的任务还在执行,所以只好等着。就像下边:
setTimeout(() => {
task()
},3000)
sleep(10000000)
四、setInterval
上面说完了setTimeout,当然不能错过它的孪生兄弟setInterval。他俩差不多,只不过后者是循环的执行。对于执行顺序来说,setInterval会每隔指定的时间将注册的函数置入Event Queue,如果前面的任务耗时太久,那么同样需要等待。
唯一需要注意的一点是,对于setInterval(fn,ms)来说,我们已经知道不是每过ms秒会执行一次fn,而是每过ms秒,会有fn进入Event Queue。一旦setInterval的回调函数fn执行时间超过了延迟时间ms,那么就完全看不出来有时间间隔了。
五、Promise与process.nextTick(callback)
process.nextTick(callback)类似node.js版的”setTimeout”,在事件循环的下一次循环中调用 callback 回调函数。
我们进入正题,除了广义的同步任务和异步任务,我们对任务有更精细的定义:
- macro-task(宏任务)包括整体代码script、setTimeout、setInterval
- micro-task(微任务)Promise、process.nextTick
不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue。
事件循环的顺序,决定JS代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中的任务队列执行完毕,再次执行所有的微任务。