JS执行机制的含义就是JS中代码的执行顺序,下面我们来分析JS代码的执行顺序。 JS的执行机制可以概括为一句话:
每执行完一次宏任务,就会执行清空一次微任务队列
问题来了:宏任务是什么,微任务是什么?
宏任务
宏任务包括同步任务和异步任务中的setTimeOut、setInterval;
微任务
微任务是除setTimeOut、setInterval之外的其他所有异步任务;
问题又来了:同步任务是什么,异步任务是什么?
同步任务
JS是一门单线程的语言,在主线程中执行的任务就是同步任务,同步任务在执行的时候,会形成一个执行栈;
异步任务
类似于ajax等需要等待回调结果的函数,无法在主线程中执行,而是进入Event Table(事件表),并在任务完成后,将回调函数注册进Event Queue(事件队列)中。像这种无法在主线程中立即执行,而是在Event Queue中注册回调函数的任务就是异步任务;
问题又又来了:“每执行完一次宏任务,就会清空一次微任务”到底是什么意思呢?
console.log('start');
setTimeout(() => {
console.log('setTimeout01 start');
new Promise((resolve) => {
console.log('promise02');
resolve();
}.then(() => {
console.log('promise02 then');
}))
setTimeout(() => {
console.log('setTimeout02')
},0)
console.log('setTimeouto1 end')
},1000)
new Promise((resolve) => {
console.log('promise01');
resolve();
}.then(() => {
console.log('promise01 then');
}))
console.log('end');
我们一起来分析一下上述代码的执行顺序:
第一轮事件循环
- 首先整段代码作为宏任务进入主线程:
- 遇到同步任务console.log,因此首先会打印“start”;
- 然后来到异步任务setTimeout,待异步任务执行完成,将其回调函数注册进任务队列的宏任务队列;
- 主线程中同步任务继续执行,执行到Promise,首先会打印“promise01”;
- 然后将Promise的then注册进任务队列的微任务队列;
- 继续执行,打印"end"; 至此,主线程中的宏任务执行完成;
- “宏任务执行完成,清空任务队列中的微任务”。因此来到微任务队列,本着清空的目的开始执行队列中的所有回调函数。由于此微任务队列中只有promise01,直接执行代码,打印‘promise01 then’;
- 截止目前,第一轮事件循环结束,我们回顾下整个过程:
- 整段代码作为一个宏任务进入主线程,首先遇到console,打印‘start’;
- 遇到setTimeout,待执行完成入任务队列;
- 遇到promise,打印‘promise01’,then入任务队列;
- 遇到console,打印‘end’;
- 宏任务执行完成,开始清空微任务队列;
- 只有promise01,执行代码,打印‘promise01 then’。
- 第一轮打印结果:start -- promise01 -- end -- promise01 then;
第二轮事件循环
- 来到宏任务队列,取出宏任务队列队头的函数setTimeout01,将其‘放入’主线程开始执行;
- 遇到console,打印‘setTime01 start’;
- 遇到promise,打印‘promise02’,then注册进任务队列的微任务队列;
- 遇到setTimeout,进任务队列的宏任务队列;
- 遇到console,打印‘setTime01 end’;至此,主线程中的宏任务执行完成;
- 开始清空微任务队列,只有一个promise02,因此执行代码,打印‘promise02 then’;
- 截至目前,第二轮事件循环结束,我们回顾下整个过程:
- setTimeout01作为一个宏任务进入主线程,首先遇到console,打印‘setTime01 start’;
- 遇到setTimeout02,待执行完成入任务队列;
- 遇到promise02,打印‘promise02’,then入任务队列;
- 遇到console,打印‘setTime01 end’;
- 宏任务执行完成,开始清空微任务队列;
- 只有promise02,执行代码,打印‘promise02 then’。
- 第二轮打印结果:setTime01 start -- promise02 -- setTime01 end -- promise02 then;
第三轮事件循环
- 来到宏任务队列,取出宏任务队列队头的函数setTimeout02,将其‘放入’主线程开始执行;
- 遇到console,打印‘setTimeout02’;至此,宏任务执行完成;
- 开始清空微任务队列,发现此时微任务队列为空;
- 因此,第三轮事件循环结束;
第四轮事件循环
- 首先来到宏任务队列,发现也为空,因此整个程序执行完成;
总结
- 同步任务在主线程中执行,异步任务需要进Event Table(事件表),待执行完成注册回调函数进Event Queue(事件队列);
- 从宏任务和微任务角度理解JS机制,宏任务包括同步任务和异步任务中的定时器,微任务是异步任务除定时器之外的部分;
- 通过分析上面的代码,我们发现JS的执行机制可以概括为:每执行完一次宏任务,就会清空一次微任务队列。