因为js是单线程语言,所以我们需要搞懂js的执行机制。不要以为js是单线程语言,你以为的js就是这样的

    1. let a = 'aaa';
    2. console.log(a);
    3. let b = 'bbb';
    4. console.log(b);

    然后实际上我们经常碰到这样的:

    1. setTimeout(function() {
    2. console.log("定时器开始了")
    3. })
    4. new Promise(function(resolve){
    5. console.log("执行for循环")
    6. for(var i = 0; i < 10000; i++) {
    7. i == 99 && resolve()
    8. }
    9. }).then(function(){
    10. console.log("执行then函数")
    11. })
    12. console.log("代码执行结束")

    验证下你就想说,说好的一行一行执行呢?
    所以有必要了解一下js的执行机制

    一、关于js的执行机制
    JS是一本单线程语言,HTML5提出了Web-Worker,模拟了多线程了,但是仍然改变了JS是单线程的本质。

    二、js的事件循环
    单线程很容易造成任务执行的阻塞,所以提出了任务了分类:

    • 同步任务
    • 异步任务

    打开应用时,网页渲染过程就是一大堆同步任务,比如网页骨架、页面元素的渲染。然而一些图片耗时加载的任务就会是异步任务。
    JavaScript执行机制 - 图1
    当指定的任务完成时,Event Table会将这个函数移入Event Queue。
    主任务内的任务执行完毕为空会去Event Queue读取对应的函数,进入主线程。
    上述过程不断重复,也就是Event Loop(事件循环)。

    问:我们怎么知道主线程有没有为空呢?
    js引擎中存在monitoring process进程。

    三、setTimeout
    威名远扬的setTimeout给人的印象就是异步、可以延迟执行

    1. setTimeout(()=> {
    2. console.log("延迟3秒执行")
    3. }, 3000)

    问题,为什么有时候设置三秒,有时候五六秒才执行呢?
    因为3s后timeout完成,任务进入Event Queue,但是主线程的任务还在执行,所以只好等着。就像下边:

    1. setTimeout(() => {
    2. task()
    3. },3000)
    4. 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代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中的任务队列执行完毕,再次执行所有的微任务。

    JavaScript执行机制 - 图2