一.js是一門單線程的語言,js是按照語句出現(xiàn)的順序執(zhí)行的
二.Javascript事件循環(huán)
因?yàn)閖s是單線程渣窜,所有js任務(wù)要一個一個順序執(zhí)行,任務(wù)分為:
???????????? 同步任務(wù)
????????????? 異步任務(wù)
流程圖:
任務(wù)進(jìn)入執(zhí)行椃稳唬——>同步任務(wù)還是異步任務(wù)艺蝴?
同步任務(wù)——>主線程——>任務(wù)全部執(zhí)行完畢——>讀取任務(wù)隊(duì)列中的結(jié)果,進(jìn)入主線程執(zhí)行
異步任務(wù)——>event table——>注冊回調(diào)函數(shù)——>event queue——>讀取任務(wù)隊(duì)列中的結(jié)果杂穷,進(jìn)入主線程執(zhí)行
上面的過程不斷重復(fù)——就是常說的Event? Loop(事件循環(huán))
let data = [];
$.ajax({
url:www.javascript.com,
data:data,
success:() => { console.log('發(fā)送成功!'); } })
console.log('代碼執(zhí)行結(jié)束')
分析:1.ajax進(jìn)入Event Table悍缠,注冊回調(diào)函數(shù) success()
2.執(zhí)行同步任務(wù) console.log('代碼執(zhí)行結(jié)束')
3.ajax事件完成,回調(diào)函數(shù) 進(jìn)入event queue
4.主線程 從event? queue中讀取 回調(diào)函數(shù)success 并執(zhí)行
setTimeout
setTimeout(() => { task() },3000)
sleep(10000000)
分析:1.task()進(jìn)入event? table并注冊耐量,計(jì)時開始
2.執(zhí)行同步任務(wù) sleep()
3.3秒到了飞蚓,但是同步任務(wù)未完成,所以event queue中仍需等待
4.執(zhí)行完廊蜒,task()從 event queue進(jìn)入主線程執(zhí)行趴拧,延遲時間大于3秒
setTimeout(fn,0)
指某個任務(wù)在主線程最早可得的空閑時間執(zhí)行,即主線程執(zhí)行棧中的同步任務(wù)完成后山叮,立即執(zhí)行fn,0毫秒是達(dá)不到的著榴,最低是4毫秒
setInterval
會每隔指定的時間將注冊的函數(shù)置入 event? queue
setInterval(fn, ms):? 不是每過ms 會執(zhí)行一次fn,而是屁倔,沒過ms脑又,會有fn進(jìn)入event? queue,一旦fn執(zhí)行時間超過了延遲時間ms锐借,那就看不出來有時間間隔了
Promise 與 process.nextTick(callback)
process.nextTick(callback)類似 node.js版的“setTimeout”问麸,在事件循環(huán)的下一次循環(huán)中調(diào)用callback回調(diào)函數(shù)
任務(wù)更精細(xì)的定義:
宏任務(wù): 包括整體代碼script, setTimeout瞎饲, setInterval
微任務(wù):promise, process.nextTick
事件循環(huán)循序:進(jìn)入整體代碼(宏任務(wù))后炼绘,開始第一次循環(huán)嗅战,接著執(zhí)行所有的微任務(wù),然后再次從宏任務(wù)開始俺亮,找到其中一個任務(wù)隊(duì)列執(zhí)行完畢驮捍,再執(zhí)行所有的微任務(wù)
setTimeout(function() { console.log('setTimeout'); })
new Promise(function(resolve) { console.log('promise');
}).then(function() { console.log('then'); })
console.log('console')
// promise?
//console
//undefined
//setTimeout
分析:1.這段代碼作為宏任務(wù),進(jìn)入主線程
2.先遇到settimout,那么將其回調(diào)函數(shù)注冊后分發(fā)到宏任務(wù)event queue
3.接下來promise脚曾,立即執(zhí)行东且,并將then函數(shù)分發(fā)到微任務(wù)event queue
4.遇到 console.log并執(zhí)行
5.整體代碼script作為第一個宏任務(wù)執(zhí)行結(jié)束,看看有哪些微任務(wù)本讥?then函數(shù)在微任務(wù)中珊泳,并執(zhí)行
6.第一輪事件循環(huán)結(jié)束后鲁冯,開始第二輪循環(huán),從宏任務(wù)event queue開始色查,發(fā)現(xiàn) 了宏任務(wù) setimout對應(yīng)的回調(diào)函數(shù)薯演,立即執(zhí)行
7.結(jié)束
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') }) })
分析
第一輪事件循環(huán)流程分析如下:
整體script作為第一個宏任務(wù)進(jìn)入主線程,遇到console.log秧了,輸出1跨扮。
遇到setTimeout,其回調(diào)函數(shù)被分發(fā)到宏任務(wù)Event Queue中验毡。我們暫且記為setTimeout1衡创。
遇到process.nextTick(),其回調(diào)函數(shù)被分發(fā)到微任務(wù)Event Queue中晶通。我們記為process1璃氢。
遇到Promise遮斥,new Promise直接執(zhí)行墩朦,輸出7。then被分發(fā)到微任務(wù)Event Queue中匿又。我們記為then1隘竭。
又遇到了setTimeout塘秦,其回調(diào)函數(shù)被分發(fā)到宏任務(wù)Event Queue中,我們記為setTimeout2动看。
宏任務(wù)Event Queue微任務(wù)Event Queue
setTimeout1process1
setTimeout2then1
上表是第一輪事件循環(huán)宏任務(wù)結(jié)束時各Event Queue的情況尊剔,此時已經(jīng)輸出了1和7。
我們發(fā)現(xiàn)了process1和then1兩個微任務(wù)菱皆。
執(zhí)行process1,輸出6须误。
執(zhí)行then1,輸出8仇轻。
好了京痢,第一輪事件循環(huán)正式結(jié)束,這一輪的結(jié)果是輸出1篷店,7祭椰,6,8疲陕。那么第二輪時間循環(huán)從setTimeout1宏任務(wù)開始:
首先輸出2方淤。接下來遇到了process.nextTick(),同樣將其分發(fā)到微任務(wù)Event Queue中蹄殃,記為process2携茂。new Promise立即執(zhí)行輸出4,then也分發(fā)到微任務(wù)Event Queue中诅岩,記為then2讳苦。
宏任務(wù)Event Queue微任務(wù)Event Queue
setTimeout2process2
then2
第二輪事件循環(huán)宏任務(wù)結(jié)束带膜,我們發(fā)現(xiàn)有process2和then2兩個微任務(wù)可以執(zhí)行。
輸出3医吊。
輸出5钱慢。
第二輪事件循環(huán)結(jié)束,第二輪輸出2卿堂,4束莫,3,5草描。
第三輪事件循環(huán)開始览绿,此時只剩setTimeout2了,執(zhí)行穗慕。
直接輸出9饿敲。
將process.nextTick()分發(fā)到微任務(wù)Event Queue中。記為process3逛绵。
直接執(zhí)行new Promise怀各,輸出11。
將then分發(fā)到微任務(wù)Event Queue中术浪,記為then3瓢对。
宏任務(wù)Event Queue微任務(wù)Event Queue
process3
then3
第三輪事件循環(huán)宏任務(wù)執(zhí)行結(jié)束,執(zhí)行兩個微任務(wù)process3和then3胰苏。
輸出10硕蛹。
輸出12。
第三輪事件循環(huán)結(jié)束硕并,第三輪輸出9法焰,11,10倔毙,12埃仪。
整段代碼,共進(jìn)行了三次事件循環(huán)陕赃,完整的輸出為1卵蛉,7,6凯正,8毙玻,2豌蟋,4廊散,3,5梧疲,9允睹,11运准,10,12缭受。
(請注意胁澳,node環(huán)境下的事件監(jiān)聽依賴libuv與前端環(huán)境不完全相同,輸出順序可能會有誤差)
總結(jié)
1.js的異步
js是一門單線程的語言米者,無論是什么新框架新語法實(shí)現(xiàn)的所謂異步韭畸,都是用同步的方法去模擬的
2.事件循環(huán)event? Loop
事件循環(huán)是js實(shí)現(xiàn)異步的一種方法,也是js的執(zhí)行機(jī)制
3.js的執(zhí)行與運(yùn)行
js在不同的環(huán)境下蔓搞,不如node胰丁,瀏覽器等,執(zhí)行方式是不同的
運(yùn)行大多指js解析引擎,是統(tǒng)一的
作者:ssssyoki
鏈接:https://juejin.im/post/59e85eebf265da430d571f89