JS中的执行机制(setTimeout、setInterval、promise、宏任务、微任务)

1、执行机制

JS 是单线程的,处理 JS 任务(程序)只能一个一个顺序执行,所以 JS 中就把任务分为了同步任务和异步任务。同步的进入主线程先执行,异步的进入Event Table并注册函数,当指定的事情完成时,Event Table会将这个函数移入事件队列Event Queue,等待主线程内的任务执行完毕,然后就会从事件队列 Event Queue 中读取对应的函数,进入主线程执行。

除了广义的同步任务和异步任务,JS 对任务还有更精细的定义:

  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval,setImmediate ,I/O ,UI rendering
  • micro-task(微任务):Promise,process.nextTick,MutationObserver

微任务先于宏任务执行(除了一开始的整体代码 script)。执行过程中,不同类型的任务会进入对应的事件队列Event Queue,比如setTimeoutsetInterval会进入相同的Event Queue。

1.1、执行优先级

  • 同步代码执行顺序优先级高于异步代码执行顺序优先级

(注意:process.nextTick 是 node 中的方法,而在浏览器中执行时(比如在vue项目中),会退化成setTimeout,所以在浏览器中 process.nextTick 会比 Promise.then() 慢)

1.2、总结

总得来说,在 JS 中,先是执行整体的同步任务代码,遇到微任务就会将其放在微任务事件队列,遇到宏任务就会放在宏任务事件队列中。

然后整体的同步任务代码执行完后,就会先执行微任务队列中的任务,等待微任务队列中的所有任务执行完毕后,此时才会从宏任务队列中找到第一个任务进行执行。该任务执行过程中,如果遇到微任务就会放到微任务队列中,等到该任务执行完后,就会查看微任务队列中有没有微任务,如果有就先执行完微队列中的任务,否则执行第二个宏任务。以此类推。

 

例如:

console.log('script start')

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

setTimeout(function() {
  console.log('setTimeout')
}, 0)

new Promise(resolve => {
  console.log('Promise')
  resolve()
})
  .then(function() {
    console.log('promise1')
  })
  .then(function() {
    console.log('promise2')
  })

console.log('script end')

 打印顺序为:

script start => async2 end => Promise => script end => async1 end => promise1 => promise2 => setTimeout

首先先来解释下上述代码的 async 和 await 的执行顺序。当我们调用 async1 函数时,会马上输出 async2 end,并且函数返回一个 Promise,接下来在遇到 await的时候会就让出线程开始执行 async1 外的代码,所以我们完全可以把 await 看成是让出线程的标志。

然后当同步代码全部执行完毕以后,就会去执行所有的异步代码,那么又会回到 await 的位置执行返回的 Promise 的 resolve 函数,这又会把 resolve 丢到微任务队列中,接下来去执行 then 中的回调,当两个 then 中的回调全部执行完毕以后,又会回到 await 的位置处理返回值,这时候你可以看成是 Promise.resolve(返回值).then(),然后 await 后的代码全部被包裹进了 then 的回调中,所以 console.log('async1 end') 会优先执行于 setTimeout

 

部分参考于:https://www.cnblogs.com/wenxuehai/p/12030199.html

posted @ 2020-10-22 10:45  阿伮  阅读(680)  评论(0编辑  收藏  举报