先说说JS的执行机制。
JS是一门单线程语言,在执行时是从上到下执行的。
Event Loop(事件循环)是JavaScript的执行机制。
1.同步任务和异步任务
JS的任务可以分为两种。一种是同步任务,一种是异步任务;
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入"任务队列"的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
(1)、同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
(2)、当Event Table中指定的事情完成时,会将这个函数移入Event Queue。
(3)、主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
(4)、上述过程会不断重复,也就是常说的Event Loop(事件循环)
2.宏任务和微任务
JS异步有一个执行机制,就是遇到宏任务,先执行宏任务,将宏任务放入eventqueue,然后在执行微任务,将微任务放入eventqueue;这两个queue不是一个queue。当你往外拿的时候先从微任务里拿这个回掉函数,然后再从宏任务的queue上拿宏任务的回掉函数。
宏任务: 包含整个script代码块,setTimeout, setIntval
微任务 :Promise , process.nextTick
为了便于理解,我在这里引入两道例题。
例1:
setTimeout(function() {
console.log('4')
})
new Promise(function(resolve) {
console.log('1') // 同步任务
resolve()
}).then(function() {
console.log('3')
})
console.log('2')
1。这段代码作为宏任务,进入主线程。
先遇到setTimeout,那么将其回调函数注册后分发到宏任务event queue.
2。接下来遇到Promise, new Promise立即执行,then函数分发到微任务event queue
遇到console.log(), 立即执行
3。整体代码script作为第一个宏任务执行结束, 查看当前有没有可执行的微任务,执行then的回调。(第一轮事件循环结束了,我们开始第二轮循环)
4。从宏任务的event queue开始,我们发现了宏任务event queue中setTimeout对应的回调函数,立即执行。
所以 执行结果: 1-2-3-4
例2:
console.log('1')
setTimeout(function() {
console.log('2')
process.nextTick(function() {
console.log('3')
})
new Promise(function(resolve) {
console.log('4')
resolve()
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6')
})
new Promise(function(resolve) {
console.log('7')
resolve()
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9')
process.nextTick(function() {
console.log('10')
})
new Promise(function(resolve) {
console.log('11')
resolve()
}).then(function() {
console.log('12')
})
})
1.整体script作为第一个宏任务进入主线程,遇到console.log(1)输出1
2。遇到setTimeout, 其回调函数被分发到宏任务event queue中。我们暂且记为setTimeout1
3。 遇到process.nextTick(),其回调函数被分发到微任务event queue中,我们记为process1
4。遇到Promise, new Promise直接执行,输出7.then被分发到微任务event queue中,我们记为then1
5。又遇到setTimeout,其回调函数被分发到宏任务event queue中,我们记为setTimeout2.
6。现在开始执行微任务, 我们发现了process1和then1两个微任务,执行process1,输出6,执行then1,输出8, 第一轮事件循环正式结束, 这一轮的结果输出1,7,6,8.那么第二轮事件循环从setTimeout1宏任务开始
7。首先输出2, 接下来遇到了process.nextTick(),统一被分发到微任务event queue,记为process2
8。new Promise立即执行,输出4,then也被分发到微任务event queue中,记为then2
9。现在开始执行微任务,我们发现有process2和then2两个微任务可以执行输出3,5. 第二轮事件循环结束,第二轮输出2,4,3,5. 第三轮事件循环从setTimeout2哄任务开始
10。 直接输出9,跟第二轮事件循环类似,输出9,11,10,12
完整输出是1,7,6,8,2,4,3,5,9,11,10,12