美文网首页
JavaScript执行机制

JavaScript执行机制

作者: wojiaho | 来源:发表于2019-03-11 10:40 被阅读0次

    首先,看一道面试题

    //请写出输出内容
    async function async1() {
        console.log('async1 start');
        await async2();
        console.log('async1 end');
    }
    async function async2() {
        console.log('async2');
    }
    
    console.log('script start');
    
    setTimeout(function() {
        console.log('setTimeout');
    }, 0)
    
    async1();
    
    new Promise(function(resolve) {
        console.log('promise1');
        resolve();
    }).then(function() {
        console.log('promise2');
    });
    console.log('script end');
    /*
    script start
    async1 start
    async2
    promise1
    script end
    async1 end
    promise2
    setTimeout
    */
    

    这道题目需要我们了解js的运行机制

    单线程

    js的一大特点就是单线程,也就是说同一时间只能做一件事情。
    js单线程的原因就是它是一个脚本语言,主要用于用户交互以及操作DOM。如果是多线程,那么如果一个线程删除了DOM,另一个线程又在操作这个DOM,那么应该以哪个为准呢?所以他的用途决定了它只能是单线程

    任务队列

    单线程就表示所有的任务都要排队,只有前面的任务执行完毕之后才能执行下一个任务。
    有的时候因为ajax请求等任务需要等待的时间很长,如果要等到请求完成在执行下一个任务,会浪费大量时间。此时js的设计者就想到了,可以将这些任务挂起,继续执行下面的任务,等到请求有了结果再去执行挂起的任务。
    于是所有的任务可以分成同步任务和异步任务。同步任务指的是在主线程上执行,只有当前任务执行完毕才能执行下一个任务。异步任务是指不进入主线程,而进入"任务队列"的任务。当异步任务有了运行结果,在可运行的异步任务添加到执行栈中
    所以我们要明白以下几点:

    1. js分为同步任务和异步任务
    2. 同步任务都在主线程中形成,形成一个执行栈
    3. 异步任务会存放在任务队列中,当异步任务有了运行结果,就放在任务队列中放置一个事件
    4. 当执行栈中所有的同步任务执行完毕之后,就会去执行任务队列中放置的任务
    5. 以上过程会重复执行

    宏任务

    (macro)task也就是宏任务:我们可以认为每次在执行栈中执行的代码就是一个宏任务
    (macro)task包括:script(整体代码)、setTimeOut、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(Node.js环境)

    微任务

    microtask也就是微任务:当前task结束之后立即执行的任务。这些任务响应速度比setTimeout会更快,因为无需等待渲染。
    microtask主要包括:Promise.then、MutaionObserver、process.nextTick(Node.js环境)

    事件循环

    主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这个运行机制叫事件循环
    在事件循环中,每一次循环成为一个tick,每次tick任务处理模型比较复杂,但是关键步骤如下:

    • 执行一个宏任务(栈中没有就从事件队列中获取)
    • 执行中遇到了微任务,就放到微任务队列中
    • 宏任务执行完毕之后,去微任务队列中依次执行所有的微任务
    • 当前宏任务执行完毕之后,开始检查渲染,然后GUI线程接管渲染
    • 渲染完毕之后,js线程继续接管,开始下一个宏任务

    Promise和async

    Promise和async的异步体现在then和catch中,所以写在Promise和async中的代码会立即执行
    async本身就是promise+generator的语法糖。所以await后面的代码就是microtask

    async function async1() {
        console.log('async1 start');
        await async2();
    }
    

    等价于

    async function async1() {
        console.log('async1 start');
        Promise.resolve(async2()).then(() => {
            console.log('async1 end');
        })
    }
    

    代码分析

    1.首先将整个代码(script)压如执行栈


    微信图片_20190311100100.png
    2.然后定义了两个async方法,接着是一个console,将console压栈,然后输出script start 微信图片_20190311100535.png
    3.接着遇到setTimeout,这是一个宏任务
    微信图片_20190311101036.png
    4.接着执行async1,async1中的第一个console直接入栈,输出async1 start,然后执行await后面的async2,输出async2,await之后的代码就是一个微任务 微信图片_20190311102424.png
    5.下面遇到promise,promise的异步体现在then中,所以promise中的代码立即压栈,执行console,输出promise1,将then中的代码放入到微任务中,然后执行最后一句console,输出script end
    微信图片_20190311103238.png
    6.这时候第一宏任务已经全部执行完毕,此时我们执行微任务队列中的两个console,依次输出async1 end 和promise2
    微信图片_20190311103708.png
    7.然后执行下一个宏任务,也就是setTimeout,输出setTimeout
    微信图片_20190311103908.png

    参考文献:
    1.https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7
    2.http://www.ruanyifeng.com/blog/2014/10/event-loop.html

    相关文章

      网友评论

          本文标题:JavaScript执行机制

          本文链接:https://www.haomeiwen.com/subject/azzzuqtx.html