• JS的执行机制


    先看段代码

    console.log(1) // 同步
    setTimeout(function() {// 异步
        console.log(2)
    }, 10)
    new Promise((resolve) => {
        console.log('promise') // 同步
        resolve()
    }).then(() => {
        console.log('then') // 异步
    })
    console.log(3) // 同步
    // 执行栈中,同步先执行,输出 1
    // 异步任务的回调函数加入到任务队列。(console.log(2)和console.log('then'))
    // 接着同步任务,输出 promise 和 3
    // 同步任务执行完成,如果异步任务有了结果(10ms过后)
    // 将任务队列中异步的回调函数加入到执行栈中执行,输出 2 then(队列先进先出)
    // 理想结果:1 promise 3 2 then
    // 正确输出:1 promise 3 then 2

    结果显然不对,两个异步任务console.log(2)console.log('then')执行顺序错了,队列不是先进先出的吗?2先进去,then后进去,为什么结果却反过来了?它们都是异步任务,这时单单拿同步和异步来解释已经解释不同了。

    所以,就引出了微任务和宏任务===>===>===>===>===>===>===>===>===>===>===>===>

    微任务:Promise,process.nextTick

    宏任务:整体代码script,setTimeout,setInterval

    执行机制变成:先执行整体代码(宏任务),执行完成后,执行微任务,微任务执行完,第一轮事件循环完毕;开启第二轮:读取任务队列,看是否有宏任务,如果有就执行……

    上面代码,因为promise是微任务,所以它优先于setTimeout执行

    下面来详细了解一下什么是宏任务,什么是微任务吧===>===>===>===>===>=====>===>===>===>

    宏任务:

    所谓的宏任务,就是需要其它线程处理的任务,这里的其它线程包括JS引擎线程,事件触发线程,定时器线程,异步网络请求线程,所以一旦和上面4个线程挂钩,它就是宏任务,我们拿上面列出的宏任务———解释下:

      整体代码script(JS引擎线程)

      setTimeout,setInterval(定时器线程)

      ajax异步请求(异步网络请求线程)

      onclick事件绑定(事件触发线程)

    是不是比上面给出的宏任务种类多?所以,记住:凡是涉及到浏览器内核线程的任务,都是宏任务。

    微任务:

    微任务通常来说就是需要在当前任务执行结束后立即执行的任务,比如异步的任务但又不需要JS引擎线程处理的任务(除字很关键!)

    首先它是异步的,其次,它不需要诸如事件触发进程定时器线程异步网络请求线程来处理。来看看Promise,process.nextTick,浏览器中有专门的的内核线程来处理吗?你可能会说它是由JS引擎线程执行的啊,确实,但它只是个异步,所以是微任务。

    你可以这么理解:不是宏任务的任务就是微任务。

    事件循环(Event-Loop):

    事件循环就是就是JavaScript的执行机制,即执行流程:

    1. 执行整段 JavaScript 代码,将整段代码中的同步任务放入执行栈中执行(上面说了,这个是宏任务)
    2. 代码中如果有 setTimeoutajax等宏任务,会利用对应的浏览器的内核线程来处理,达到条件(定时器时间达到,请求完成)后,由事件触发线程将其对应的回调加入到事件队列(任务队列)中
    3. 如果有Promise等微任务,加入微任务队列,在执行栈执行完当前的同步任务的之后,从微任务队列中取出微任务,立即执行
    4. 所有的微任务执行完,此时,执行栈中处于闲置状态(注意:本轮事件循环中所有微任务执行完,开启下轮循环)
    5. 以上是第一轮事件循环,以下开始第二轮:
    6. 事件队列将队列中的任务加入到执行栈,按先进先出的顺序执行
    7. 如果此时进入栈中的任务既有同步任务,微任务和宏任务,那先执行同步任务
    8. 再执行所有的微任务
    9. 第二轮事件循环结束,开始第三轮循环:
    10. 执行宏任务...循环往复,直到所有任务执行完毕。

    下面看段代码:

    console.log(111)
    setTimeout(function() {
        console.log(222)
    }, 0)
    new Promise((resolve) => {
        console.log('333')
        resolve()
    }).then(() => {
        console.log('444')
    })
    $.ajax({
        url: '',
        success: function() {
            console.log(555)
        }
    })
    console.log(666)
    1. 运行代码,先执行整段代码的同步任务console.log(111),输出 111
    2. 遇到 setTimeout,将其交给定时器线程处理,0秒后,才由事件触发线程将其回调函数交给事件队列
    3. 遇到 Promise,Promise为立即执行函数,所以输出 333
    4. 它有一个 then 回调,是个微任务,先不执行
    5. 遇到 ajax请求,将其交给异步网络请求线程处理,请求完成响应200后,由事件触发线程将其成功的回调函数交给事件队列
    6. 此时假设ajax请求完成,故现在的事件队列为 ①setTimeout回调,②ajax回调
    7. 执行同步代码console.log(666),输出 666
    8. 同步任务执行完,查看是否有微任务,有微任务,为 then 回调,开始执行,输出 444
    9. 没有其他微任务了,此时执行栈为空,第一轮事件循环结束,准备开始第二轮事件循环
    10. 去事件队列中取任务
    11. 首先取出setTimeout的回调,输出 222
    12. 然后取出ajax的回调,输出 555
    13. 结果:111 333 666 444 222 555
  • 相关阅读:
    去重
    JavaScript 数组
    变量
    2017.3.19 for
    2017.3.19 if,for
    2017.3.14
    2017.3.10
    2017.3.9
    2017.3.7-1
    2017.3.7
  • 原文地址:https://www.cnblogs.com/lhy-555/p/11951033.html
Copyright © 2020-2023  润新知