摘要:主線程在任務(wù)隊(duì)列中讀取事件,這個(gè)過(guò)程是循環(huán)不斷地,所以這種運(yùn)行機(jī)制叫做事件循環(huán)是在執(zhí)行棧同步代碼結(jié)束之后,下一次任務(wù)隊(duì)列執(zhí)行之前。
單線程
javascript為什么是單線程語(yǔ)言,原因在于如果是多線程,當(dāng)一個(gè)線程對(duì)DOM節(jié)點(diǎn)做添加內(nèi)容操作的時(shí)候,另一個(gè)線程要?jiǎng)h除這個(gè)DOM節(jié)點(diǎn),這個(gè)時(shí)候,瀏覽器應(yīng)該怎么選擇,這就造成了混亂,為了解決這類問(wèn)題,在一開(kāi)始的時(shí)候,javascript就采用單線程模式。
在后面H5出的web worker標(biāo)準(zhǔn)的時(shí)候,看似是多線程,其實(shí)是在一個(gè)主線程來(lái)控制其他線程,而且不能操作DOM,所以本質(zhì)還是單線程
任務(wù)隊(duì)列任務(wù)可以分為兩種,一種為同步,另一種為異步(具有回調(diào)函數(shù))。如下圖:
所有的同步任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧 stack。當(dāng)所有同步任務(wù)執(zhí)行完畢后,它會(huì)去執(zhí)行microtask queue中的異步任務(wù)(nextTick,Promise),將他們?nèi)繄?zhí)行。主線程之外還有一個(gè)任務(wù)隊(duì)列task queue,當(dāng)有異步任務(wù)(DOM,AJAX,setTimeout,setImmediate)有結(jié)果的時(shí)候,就在任務(wù)隊(duì)列里放一個(gè)事件,一旦執(zhí)行棧和microtask queue任務(wù)執(zhí)行完畢,系統(tǒng)就會(huì)讀取任務(wù)隊(duì)列,將取出排在最前面的事件加入執(zhí)行棧執(zhí)行,這種機(jī)制就是任務(wù)隊(duì)列。
主線程在任務(wù)隊(duì)列中讀取事件,這個(gè)過(guò)程是循環(huán)不斷地,所以這種運(yùn)行機(jī)制叫做Event Loop(事件循環(huán))
nextTick、setImmediate、setTimeoutnextTick是在執(zhí)行棧同步代碼結(jié)束之后,下一次Event Loop(任務(wù)隊(duì)列)執(zhí)行之前。當(dāng)所有同步任務(wù)執(zhí)行完,會(huì)在queue中執(zhí)行nextTick,無(wú)論nextTick有多少層回調(diào),都會(huì)執(zhí)行完畢后再去任務(wù)隊(duì)列,所以會(huì)造成一直停留在當(dāng)前執(zhí)行棧,無(wú)法執(zhí)行任務(wù)隊(duì)列,請(qǐng)看下面代碼
process.nextTick(function () { console.log("nextTick1"); process.nextTick(function (){console.log("nextTick2")}); }); setTimeout(function timeout() { console.log("setTimeout"); }, 0)
執(zhí)行完畢后輸出nextTick1、nextTick2、setTimeout,原因是nextTick是在當(dāng)前執(zhí)行棧末尾執(zhí)行,而setTimeout是在下次任務(wù)隊(duì)列在執(zhí)行
setImmediate方法是在Event Loop(任務(wù)隊(duì)列)末尾,也就是下一次Event Loop時(shí)執(zhí)行。
setTimeout方法是按照?qǐng)?zhí)行時(shí)間,放入任務(wù)隊(duì)列,有時(shí)快與setImmediate有時(shí)慢。請(qǐng)看以下代碼
setImmediate(function () { console.log("setImmediate1"); setImmediate(function (){console.log("setImmediate2")}); }); setTimeout(function timeout() { console.log("setTimeout"); }, 0);
這段代碼執(zhí)行完可能是setImmediate1、setTimeout、setImmediate2,也可能是setTimeout、setImmediate1、setImmediate2,原因是setTimeout和setImmediate1都是在下次Event Loop中觸發(fā),所以先后不確定,但是setImmediate2肯定是最后,因?yàn)樗窃趕etImmediate1任務(wù)隊(duì)列之后,也就是下下次Event Loop執(zhí)行
Node.js的Event LoopNode.js也是單線程的Event Loop但是和瀏覽器有些區(qū)別,如圖所示,
1.先通過(guò)Chrom V8引擎解析Javascript腳本
2.解析完畢后調(diào)用Node API
3.LIBUV庫(kù)負(fù)責(zé)Node API的執(zhí)行,將不同任務(wù)分配給不同的線程,形成一個(gè)Event Loop(任務(wù)隊(duì)列)
4.最后Chrom V8引擎將結(jié)果返回給用戶
node.js的特點(diǎn)是事件驅(qū)動(dòng),非阻塞單線程。當(dāng)應(yīng)用程序需要I/O操作的時(shí)候,線程并不會(huì)阻塞,而是把I/O操作交給底層庫(kù)(LIBUV)。此時(shí)node線程會(huì)去處理其他任務(wù),當(dāng)?shù)讓訋?kù)處理完I/O操作后,會(huì)將主動(dòng)權(quán)交還給Node線程,所以Event Loop的用處是調(diào)度線程,例如:當(dāng)?shù)讓訋?kù)處理I/O操作后調(diào)度Node線程處理后續(xù)工作,所以雖然node是單線程,但是底層庫(kù)處理操作依然是多線程
Node Event Loop的事件處理機(jī)制┌───────────────────────┐ ┌─>│ timers │ │ └──────────┬────────────┘ │ ┌──────────┴────────────┐ │ │ I/O callbacks │ │ └──────────┬────────────┘ │ ┌──────────┴────────────┐ │ │ idle, prepare │ │ └──────────┬────────────┘ ┌───────────────┐ │ ┌──────────┴────────────┐ │ incoming: │ │ │ poll │<─────┤ connections, │ │ └──────────┬────────────┘ │ data, etc. │ │ ┌──────────┴────────────┐ └───────────────┘ │ │ check │ │ └──────────┬────────────┘ │ ┌──────────┴────────────┐ └──┤ close callbacks │ └───────────────────────┘
上面處理階段都是按照先進(jìn)先出的規(guī)則執(zhí)行回調(diào)函數(shù),按順序執(zhí)行,直到隊(duì)列為空或是該階段執(zhí)行的回調(diào)函數(shù)達(dá)到該階段所允許一次執(zhí)行回調(diào)函數(shù)的最大限制后,才會(huì)將操作權(quán)移交給下一階段。
timers: 用來(lái)檢查setTimeout()和setInterval()定時(shí)器是否到期,如果到期則執(zhí)行它,否則下一階段
I/O callbacks: 用來(lái)處理timers階段、setImmediate、和TCP他們的異常回調(diào)函數(shù)或者error
idle, prepare: nodejs內(nèi)部函數(shù)調(diào)用,在循環(huán)被I/O阻塞之前prepare回調(diào)就會(huì)立即調(diào)用
poll: 用來(lái)監(jiān)聽(tīng)fd的事件的,比如socket的可讀,可寫(xiě),文件的可讀可等等
check: setImmediate()函數(shù)只會(huì)在這個(gè)階段執(zhí)行
close callbacks: 執(zhí)行一些諸如關(guān)閉事件的回調(diào)函數(shù),如socket.on("close", ...)
具體分析,看下圖:1.當(dāng)setTimeout時(shí)間最小,讀取文件不存在的時(shí)候
如圖所示,分別是nextTick、readFile、setTimeout、setImmediate,然而現(xiàn)在并沒(méi)有1.txt和2.txt文件,輸出結(jié)果是next Tick、setTimeout、readFile、setImmediate,在event loop中先判斷的是timeers,最先出書(shū)next Tick因?yàn)閜rocess.nextTick的實(shí)現(xiàn)是基于v8 MicroTask(是在當(dāng)前js call stack 中沒(méi)有可執(zhí)行代碼才會(huì)執(zhí)行的隊(duì)列,低于js call stack 代碼,但高于事件循環(huán),不屬于Event Loop,上面javascript的Event Loop介紹過(guò)了,所以最先輸出。然后開(kāi)始走Event Loop,第一階段是timers,判斷setTimeout到期,所以輸出setTimeout,進(jìn)入下一階段,poll將I/O操作權(quán)交出,新線程操作,但是并沒(méi)有相關(guān)讀取文件,所以直接返回回調(diào)函數(shù),所以處處readFile,最后到check階段,輸出setImmediate
2.當(dāng)setTimeout時(shí)間最小,讀取文件存在的時(shí)候
如圖所示,分別是nextTick、setTimeout、setImmediate、readFile,這次readFile在最后面,是因?yàn)槲募嬖?,?zhí)行到poll階段的時(shí)候,執(zhí)行I/O操作,node線程開(kāi)始執(zhí)行check階段,當(dāng)交出的I/O操作結(jié)束后,返回給Event Loop所以再執(zhí)行readFile的回調(diào)函數(shù),所以他在最后面
3.當(dāng)setTimeout時(shí)間為100毫秒,讀取文件不存在的時(shí)候
如圖所示,分別是nextTick、readFile、setImmediate、setTimeout,它和1不同的地方是setTimeout排在最后了,這是因?yàn)樵趫?zhí)行timers的時(shí)候,setTimeout沒(méi)有到期,所以直接執(zhí)行下一階段,當(dāng)執(zhí)行完poll的時(shí)候,會(huì)去執(zhí)行查看定時(shí)器有沒(méi)有到期,如果沒(méi)有下一次Event Loop再次查看,知道定時(shí)器到期,所以他在最后面
4.當(dāng)setTimeout時(shí)間為100毫秒,讀取文件存在的時(shí)候
如圖所示,分別是nextTick、setImmediate、readFile、setTimeout,它和2的區(qū)別是setTimeout在最后,原因和3一樣。
1.javascript和node.js都是單線程,但是node底層是多線程操作
2.Event Loop —— 任務(wù)隊(duì)列
3.當(dāng)同時(shí)設(shè)置nextTick, setImmediate, setTimeout時(shí)一定是nextTick先執(zhí)行,nextTick不屬于Event LOop,它屬于v8的micro tasks,并且會(huì)阻塞Event Loop
4.setImmediate,setTimeout屬于Event Loop但是,直接階段不同
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/96565.html
摘要:中線程運(yùn)行機(jī)制詳解對(duì)于我們都知道,他是個(gè)單線程語(yǔ)言,但是準(zhǔn)確來(lái)說(shuō)它是擁有一個(gè)執(zhí)行程序主線程,和消息隊(duì)列輔線程,以及各個(gè)真正處理異步操作的工作線程。 JavaScript中線程運(yùn)行機(jī)制詳解 對(duì)于JavaScript我們都知道,他是個(gè)單線程語(yǔ)言,但是準(zhǔn)確來(lái)說(shuō)它是擁有一個(gè)執(zhí)行程序主線程,和消息隊(duì)列輔線程(Event Loop),以及各個(gè)真正處理異步操作的工作線程。當(dāng)主線程執(zhí)行JS程序的時(shí)候,...
摘要:上代碼代碼可以看出,不僅函數(shù)比指定的回調(diào)函數(shù)先執(zhí)行,而且函數(shù)也比先執(zhí)行。這是因?yàn)楹笠粋€(gè)事件進(jìn)入的時(shí)候,事件環(huán)可能處于不同的階段導(dǎo)致結(jié)果的不確定。這是因?yàn)橐驗(yàn)閳?zhí)行完后,程序設(shè)定了和,因此階段不會(huì)被阻塞進(jìn)而進(jìn)入階段先執(zhí)行,后進(jìn)入階段執(zhí)行。 JavaScript(簡(jiǎn)稱JS)是前端的首要研究語(yǔ)言,要想真正理解JavaScript就繞不開(kāi)他的運(yùn)行機(jī)制--Event Loop(事件環(huán)) JS是一門(mén)...
摘要:機(jī)制詳解與中實(shí)踐應(yīng)用歸納于筆者的現(xiàn)代開(kāi)發(fā)語(yǔ)法基礎(chǔ)與實(shí)踐技巧系列文章。事件循環(huán)機(jī)制詳解與實(shí)踐應(yīng)用是典型的單線程單并發(fā)語(yǔ)言,即表示在同一時(shí)間片內(nèi)其只能執(zhí)行單個(gè)任務(wù)或者部分代碼片。 JavaScript Event Loop 機(jī)制詳解與 Vue.js 中實(shí)踐應(yīng)用歸納于筆者的現(xiàn)代 JavaScript 開(kāi)發(fā):語(yǔ)法基礎(chǔ)與實(shí)踐技巧系列文章。本文依次介紹了函數(shù)調(diào)用棧、MacroTask 與 Micr...
摘要:曾經(jīng)的理解首先,是單線程語(yǔ)言,也就意味著同一個(gè)時(shí)間只能做一件事,那么為什么不是多線程呢這樣還能提高效率啊假定同時(shí)有兩個(gè)線程,一個(gè)線程在某個(gè)節(jié)點(diǎn)上編輯了內(nèi)容,而另一個(gè)線程刪除了這個(gè)節(jié)點(diǎn),這時(shí)瀏覽器就很懵逼了,到底以執(zhí)行哪個(gè)操作呢所以,設(shè)計(jì)者把 Event Loop曾經(jīng)的理解 首先,JS是單線程語(yǔ)言,也就意味著同一個(gè)時(shí)間只能做一件事,那么 為什么JavaScript不是多線程呢?這樣還能提...
摘要:主線程要明確的一點(diǎn)是,主線程跟執(zhí)行棧是不同概念,主線程規(guī)定現(xiàn)在執(zhí)行執(zhí)行棧中的哪個(gè)事件。主線程循環(huán)即主線程會(huì)不停的從執(zhí)行棧中讀取事件,會(huì)執(zhí)行完所有棧中的同步代碼。以上參考資料詳解中的事件循環(huán)機(jī)制中的事件循環(huán)運(yùn)行機(jī)制詳解再談 showImg(https://segmentfault.com/img/remote/1460000015317437?w=1920&h=1080); 前言 大家都...
閱讀 2448·2021-11-12 10:34
閱讀 1500·2019-08-29 16:15
閱讀 2707·2019-08-29 15:17
閱讀 1386·2019-08-23 17:09
閱讀 414·2019-08-23 11:37
閱讀 2478·2019-08-23 10:39
閱讀 515·2019-08-22 16:43
閱讀 3140·2019-08-22 14:53