853-1920x1080.jpg

    在我们的日常开发中或者面试,我们都会遇到这样的问题:给定的几行代码,我们需要知道其输出内容和顺序。因为javascript是一门单线程语言,所以我们可以得出结论: javascript是按照语句出现的顺序执行的

    看到这里是不是想打人,难道我还不知道js是一行一行执行的?不急不急,请把你的40米长刀收起来
    微信截图_20191205094253.png
    我们以为的js是这样的:

    1. const A = '我是第一个输出';
    2. console.log(A);
    3. const B = '我是第二个输出';
    4. console.log(B);


    多么舒服,然鹅现实js是这样的:

    console.log('我是第一个');
    
    setTimeout(function(){
        console.log('我是第二个');
    });
    
    new Promise(function(resolve){
        console.log('我是第三个');
        resolve();
    }).then(function(){
        console.log('我是第四个');
    });
    
    console.log('我是第五个');
    


    依照js是一行一行执行,理想中的结果是:

    //"我是第一个"
    //"我是第二个"
    //"我是第三个"
    //"我是第四个"
    //"我是第五个"
    


    然鹅实际上的输出结果是:

    //'我是第一个'
    //'我是第三个'
    //'我是第五个'
    //'我是第四个'
    //'我是第二个'
    


    接下来,我们来谈谈javascript的执行机制

    js是一门单线程的语言,所有的任务都是需要一个一个按顺序去执行,而任务又分为两类:

    • 同步任务
    • 异步任务


    微信截图_20191205100306.png

    • 同步和异步任务分别进入不同的执行”场所”,同步的进入主线程,异步的进入Event Table并注册函数。
    • 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
    • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
    • 上述过程会不断重复,也就是常说的Event Loop(事件循环)。


    还是上面那段代码,提取一部分:

    new Promise(function(resolve){
        console.log('我是第一个');
        resolve();
    }).then(function(){
        console.log('我是第二个');
    });
    
    console.log('我是第三个');
    


    遇到Promise进入Event Table立即执行并注册函数then,then函数分发到Event Queue
    遇到console.log(),立即执行。
    主线程内的任务执行完毕,去Event Queue读取对应的函数then,进入主线程执行

    输出的结果:

    //'我是第一个'
    //'我是第三个'
    //'我是第二个'
    


    对于同步任务和异步任务,我们还有更精细的定义:

    • 宏任务:整体代码script,setTimeout,setInterval
    • 微任务:Promise,process.nextTick


    微信截图_20191205110101.png

    console.log('我是第一个');
    
    setTimeout(function(){
        console.log('我是第二个');
          new Promise(function(resolve) {
            console.log('我是第三个');
            resolve();
        }).then(function() {
            console.log('我是第四个')
        })
    });
    
    new Promise(function(resolve){
        console.log('我是第五个');
        resolve();
    }).then(function(){
        console.log('我是第六个');
    }).then(function(){
            console.log('我是第七个');
    });
    
    console.log('我是第八个');
    
    • 代码作为宏任务进入主线程
    • 遇到console.log(‘我是第一个’),立即执行
    • 遇到setTimeout,将其回调函数注册后分发到宏任务Event Queue队列中。
    • 遇到了new Promise立即执行console.log(‘我是第五个’),then函数分发到微任务Event Queue队列中
    • 遇到console.log(‘我是第八个’),立即执行
    • 第一个宏任务执行结束,看看微任务队列中有没有任务,发现有then,执行 console.log(‘我是第六个’),then函数分发到微任务队列中
    • 第二轮循环,发现微任务队列中的then,执行 console.log(‘我是第七个’)
    • 第三轮循环,发现没有了微任务,主线任务完成,进入第一个setTimeout宏任务Event Queue执行回调函数,遇到了 console.log(‘我是第二个’),执行
    • 遇到了new Promise立即执行console.log(‘我是第三个’),then函数分发到微任务Event Queue
    • 第四轮循环,发现宏任务setTimeout回调函数中还有微任务then,执行console.log(‘我是第四个’)
    • 完毕


    输出结果:

    我是第一个
    我是第五个
    我是第八个
    我是第六个
    我是第七个
    我是第二个
    我是第三个
    我是第四个
    


    再来一个复杂一点的

    setTimeout(function() {
      console.log('2');
      new Promise(function(resolve) {
        console.log('4');
        resolve();
      }).then(function() {
        setTimeout(function(){
          console.log('5');
        })
        console.log('6');
      }).then(() => {
        console.log('7');
      }).then(()=>{
        console.log('8');
      })
      setTimeout(function(){
        console.log('9');
      })
    })
    
    • 进入宏任务setTimeout,执行回调,console.log(‘2’)
    • 遇到new Promise,执行console.log(‘4’),then发放到微任务队列中
    • 遇到第一个setTimeout,发放到宏任务队列中
    • 第二次循环,发现微任务then,执行回调,发现第二个setTimeout,发放到宏任务队列中,执行console.log(‘6’),then发放到微任务中
    • 第三次循环,发现微任务then,执行console.log(‘7’),then发放到微任务队列中
    • 第四次循环,发现微任务then,执行console.log(‘8’)
    • 第五次循环,微任务队列中没有任务,执行第一个setTimeout回调,console.log(‘9’)
    • 第六次循环,微任务队列中没有任务,执行第二个setTimeout回调,console.log(‘5’)
    • 完毕


    可以再继续尝试:

      setTimeout(function() {
        console.log('2');
        new Promise(function(resolve) {
            console.log('4');
            resolve();
        }).then(function() {
          setTimeout(function(){
            console.log('5');
          })
          console.log('6')
        }).then(() => {
          console.log('7')
        }).then(()=>{
          console.log('8')
        })
        setTimeout(function(){
          console.log('9');
        })
        new Promise(function(resolve) {
            console.log('10');
            resolve();
        }).then(function() {
          setTimeout(function(){
            console.log('11');
          })
          console.log('12')
        }).then(() => {
          console.log('13')
        }).then(()=>{
          console.log('14')
        })
      })
    


    手动滑稽

    本文参考:https://juejin.im/post/59e85eebf265da430d571f89
    转载请标明出处,谢谢~