JavaScript 执行机制
javascript是单线程的,javascript是单线程这一核心不会改变。一切javascript版的"多线程"都是基于单线程模拟出来的;
由于单线程,所以javascript任务得一个一个执行(执行完一个然后去执行下一个),那么问题来了,如果一个任务执行需要很长很长的时间,那么下一个任务便会无休止的等待下去(页面卡死);这样是不切合实际的;为了解决这一问题, 同步任务&异步任务 出现了;
一、同步任务
后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的;
二、异步任务
每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
例如:AJax请求、定时器、事件、回调函数
三、任务执行过程
任务开始时,会先判断是同步任务还是异步任务,同步任务会进入任务主线程开始执行;异步任务会进入Event Table并注册回调函数(callback)。当异步任务完成,Event Table会把对应的回调函数放入Event Queue;当主线程的任务全部执行完毕后(js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。),会去Event Queue中读取相应的回调函数来执行;
图片取自:https://juejin.im/post/59e85eebf265da430d571f89#heading-4
上述过程会不断重复,也就是常说的Event Loop(事件循环)。
例如:
let data = [];
$.ajax({
url:www.javascript.com,
data:data,
success:(res) => {
console.log('我是ajax异步任务');
}
})
console.log('我是同步任务');
//我是同步任务
//我是ajax异步任务
ajax为异步任务,进入Event Table中,并注册了success回调函数;ajax执行完成了,该success回调函数被放入 Event Queue 中,等待主线程临幸!
当主线程的任务(console.log(‘我是同步任务’))执行成功了,再去Event Queue中读取success回调函数;
所以先打印 “我是同步任务” 在打印 “我是ajax异步任务”
四、setTimeout
setTimeout也是异步任务
setTimeout(() => {
console.log('我是setTimeout异步任务');
},3000)
console.log("我是同步任务")
//我是同步任务
...3秒后...
//我是异步任务
setTimeout为异步任务,进入Event Table中,并注册了匿名回调函数;当等待3秒后执行完成了,该回调函数被放入 Event Queue 中,等待主线程临幸!
当主线程的任务(console.log(‘我是同步任务’))执行成功了,再去Event Queue中读取回调函数执行;
所以先打印 “我是同步任务” 在打印 “我是setTimeout异步任务”
注意:setTimeout( fn , 0 ),就算定时为0,其也不是立即执行的,根据HTML的标准,最低是4毫秒。
五、setInterval
setInterval和setTimeout是一样的。只不过setInterval是循环的执行。对于执行顺序来说,setInterval会每隔指定的时间将注册的函数置入Event Queue,如果前面的任务耗时太久,那么同样需要等待。
注意:对于setInterval(fn,ms)来说,,每过ms秒,会有fn进入Event Queue。一旦setInterval的回调函数fn执行时间超过了延迟时间ms,那么就完全看不出来有时间间隔了。
广义划分:同步任务和异步任务
更精细的划分:宏任务和微任务
六、宏任务(macro-task)和 微任务(micro-task)
宏任务包括:整体代码script,setTimeout,setInterval
微任务包括:Promise,process.nextTick()
setTimeout(function() {
console.log('setTimeout');
})
new Promise(function(resolve) {
console.log('promise');
}).then(function() {
console.log('then');
})
console.log('console');
进入主线程:
setTimeout的回调函数被分发到宏任务Event Queue中
new Promise 立即执行 打印 promise;then()函数进入微任务的Event Queue;
console.log(‘console’);立即执行 打印 console;
这个时候会去判断是否有微任务,如果有执行,如果没有 开始下一循环,此时存在 then()微任务,执行 打印:then;
第一轮事件循环结束了,我们开始第二轮循环,当然要从宏任务Event Queue开始。我们发现了宏任务Event Queue中setTimeout对应的回调函数,立即执行。打印setTimeout
所以 依次是:promise console then setTimeout
图片取自:https://juejin.im/post/59e85eebf265da430d571f89#heading-4
接下来分析一段复杂的代码
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')
})
})
进入主线程:
console.log(‘1’); 立即执行 打印 1
setTimeout回调函数被分发到宏任务Event Queue中,我们暂时记录为setTimeout_1
process.nextTick回调函数被分发到微任务Event Queue中,我们暂时记录为process.nextTick_1
new Promise立即执行,打印7,then()函数被分发到微任务Event Queue中,我们暂时记录为then_1
setTimeout回调函数被分发到宏任务Event Queue中,我们暂时记录为setTimeout_2
此时会去判断是否有微任务,如果有执行,如果没有 开始下一循环,此时存在微任务process.nextTick_1和then_1,执行 打印:6 8;
第一轮循环结束了, 目前打印的是 1 7 6 8;
从宏任务Event Queue开始第二轮循环:
console.log(‘2’);立即执行 打印 2;
process.nextTick回调函数被分发到微任务Event Queue中,我们暂时记录为process.nextTick_2
new Promise立即执行 打印 4,then()函数被分发到微任务Event Queue中,我们暂时记录为then_2
此时会去判断是否有微任务,如果有执行,如果没有 开始下一循环,此时存在微任务process.nextTick_2和then_2,执行 打印:3 5;
第二轮循环结束了, 目前打印的是 1 7 6 8 2 4 3 5 ;
从宏任务Event Queue开始第三轮循环:
console.log(‘9’);立即执行 打印 9;
process.nextTick回调函数被分发到微任务Event Queue中,我们暂时记录为process.nextTick_3
new Promise立即执行 打印 11,then()函数被分发到微任务Event Queue中,我们暂时记录为then_3
此时会去判断是否有微任务,如果有执行,如果没有 开始下一循环,此时存在微任务process.nextTick_3和then_3,执行 打印:10 12;
第三轮循环结束了, 目前打印的是 1 7 6 8 2 4 3 5 9 11 10 12;
最后:
javascript是一门单线程语言
Event Loop(事件循环)是javascript的执行机制
完!
本文参考自:
https://juejin.im/post/59e85eebf265da430d571f89#heading-4
https://www.cnblogs.com/mengxiangji/p/10913966.html
https://www.oschina.net/translate/understanding-process-next-tick?print
https://es6.ruanyifeng.com/#docs/promise