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

 

posted @ 2019-11-28 16:46  林Clown  阅读(141)  评论(0编辑  收藏  举报