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,比如setTimeout
和setInterval
会进入相同的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