前端同學(xué)應(yīng)該經(jīng)常會面臨代碼執(zhí)行順序不同帶來的問題搞疗,例如我們需要發(fā)請求拿到某些數(shù)據(jù)去渲染頁面箍镜,所以邏輯上來說我們需要先發(fā)Ajax請求贤姆,然后拿到數(shù)據(jù)渲染頁面鱼鸠,代碼可能是下面這樣的:
const data = Ajax method ...
renderByData(data)
然而猛拴,實際結(jié)果卻是頁面上獲取到的data
是undefined
,原因相信大家都知道蚀狰。
Ajax請求是異步操作愉昆,上面的代碼在使用data
的時候請求還未返回,自然是undefined
麻蹋。
除了異步問題跛溉,Node中還有很多涉及到執(zhí)行順序的方法,例如我們常用的setTimeout
、setImmediate
以及nextTick
等芳室,他們有什么區(qū)別专肪,執(zhí)行順序是什么樣的,這些你都清楚嗎堪侯?
先看一個經(jīng)典的例子:
setImmediate(function(){
console.log(1);
},0);
setTimeout(function(){
console.log(2);
},0);
new Promise(function(resolve){
console.log(3);
resolve();
console.log(4);
}).then(function(){
console.log(5);
});
console.log(6);
process.nextTick(function(){
console.log(7);
});
console.log(8);
如果你能準確說出這段代碼的執(zhí)行結(jié)果嚎尤,恭喜你可以直接關(guān)掉這篇文章了,如果不能請繼續(xù)往下看伍宦。
結(jié)果:
node test.js
// 3 4 6 8 7 5 2 1
要搞清楚結(jié)果為什么是這樣芽死,我們需要知道以下概念:
- 同步任務(wù)與異步任務(wù)
- 宏任務(wù)與微任務(wù)
同步任務(wù)與異步任務(wù)
同步任務(wù):會立即執(zhí)行的任務(wù)
異步任務(wù):不會立即執(zhí)行的任務(wù)(包括宏任務(wù)和微任務(wù))
常見的異步任務(wù)有:Ajax
請求,Dom
事件操作次洼,setTimeout
关贵,promise
的then
方法,Node
讀取文件等卖毁。
我們知道揖曾,JavaScript
是一個單線程的語言,所有語句最終都會在一個主線程上按順序執(zhí)行势篡,這意味著如果有一個任務(wù)需要很長時間執(zhí)行就會阻塞后面的任務(wù)翩肌,為了解決這個問題,異步任務(wù)應(yīng)運而生禁悠。
代碼在執(zhí)行過程中念祭,如果遇到同步任務(wù)則立即執(zhí)行,若遇到異步任務(wù)碍侦,不會立即執(zhí)行粱坤,而是會將該任務(wù)放入任務(wù)隊列中,在執(zhí)行完所有同步任務(wù)后瓷产,根據(jù)異步任務(wù)的優(yōu)先級(預(yù)置)執(zhí)行站玄。
例如,上述代碼中promise
的then
方法就是一個異步任務(wù)濒旦,主線程執(zhí)行到這里的時候不會等待請求的返回株旷,而是繼續(xù)向下執(zhí)行,在異步請求有結(jié)果時尔邓,會將then
方法中的回調(diào)放入任務(wù)隊列晾剖,主線程在執(zhí)行完所有同步任務(wù)后,會讀取任務(wù)隊列中的回調(diào)梯嗽,進入主線程執(zhí)行齿尽。
在上述代碼中,紅框標出來的部分均為同步任務(wù)灯节,因此會按照代碼順序依次立即執(zhí)行
輸出結(jié)果:
3 4 6 8 ...
宏任務(wù)與微任務(wù)
前面提到的異步任務(wù)循头,包括宏任務(wù)和微任務(wù)绵估。
它們都屬于一個隊列,主要區(qū)別在于他們的執(zhí)行順序卡骂,Event Loop的走向和取值国裳。
通俗來講,就是優(yōu)先級不同全跨。
宏任務(wù)包括:setTimeout, setInterval, setImmediate, I/O callbacks 等
優(yōu)先級: setTimeout / setInterval > I/O callbacks > setImmediate
微任務(wù)包括:process.nextTick, Promise 回調(diào)躏救,MutationObserver
優(yōu)先級: process.nextTick > Promise > MutationObserver
微任務(wù)優(yōu)先級高于宏任務(wù)
因此,在上述例子中螟蒸,實際執(zhí)行順序如下:
process.nextTick(function(){
console.log(7);
});
new Promise(function(resolve){
//
}).then(function(){
console.log(5);
});
setTimeout(function(){
console.log(2);
},0);
setImmediate(function(){
console.log(1);
},0);
故而盒使,我們會得到文章開頭出現(xiàn)的結(jié)果。
寫在最后
若到這里你依舊對Node
中的各任務(wù)優(yōu)先級懷有疑問七嫌,可移步Node.js Event Loop
或可持續(xù)關(guān)注我后續(xù)文章少办。
謝謝
參考資料:
http://www.ruanyifeng.com/blog/2018/02/node-event-loop.html
https://segmentfault.com/a/1190000008595101
https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/