摘要:引擎單線程機(jī)制首先明確,引擎是單線程機(jī)制。是單線程執(zhí)行的,無法同時(shí)執(zhí)行多段代碼。解析是單線程的,所以會(huì)先執(zhí)行再,但這個(gè)循環(huán)體是死循環(huán),所以永遠(yuǎn)不會(huì)執(zhí)行。
Javascript 引擎單線程機(jī)制
首先明確,JavaScript引擎是單線程機(jī)制。
JavaScript 是單線程執(zhí)行的,無法同時(shí)執(zhí)行多段代碼。當(dāng)某一段代碼正在執(zhí)行的時(shí)候,所有后續(xù)的任務(wù)都必須等待,形成一個(gè)任務(wù)隊(duì)列。一旦當(dāng)前任務(wù)執(zhí)行完畢,再從隊(duì)列中取出下一個(gè)任務(wù),這也常被稱為 “阻塞式執(zhí)行”。
可以理解為:只有在JS線程中沒有任何同步代碼要執(zhí)行的前提下才會(huì)執(zhí)行異步代碼
所以一次鼠標(biāo)點(diǎn)擊,或是計(jì)時(shí)器到達(dá)時(shí)間點(diǎn),或是 Ajax 請求完成觸發(fā)了回調(diào)函數(shù),這些事件處理程序或回調(diào)函數(shù)都不會(huì)立即運(yùn)行,而是立即排隊(duì),一旦線程有空閑就 執(zhí)行。假如當(dāng)前 JavaScript 線程正在執(zhí)行一段很耗時(shí)的代碼,此時(shí)發(fā)生了一次鼠標(biāo)點(diǎn)擊,那么事件處理程序就被阻塞,用戶也無法立即看到反饋,事件處理程序會(huì)被放入任務(wù)隊(duì)列,直到前面的代碼結(jié)束以后才會(huì)開始執(zhí)行。如果代碼中設(shè)定了一個(gè) setTimeout,那么瀏覽器便會(huì)在合適的時(shí)間,將代碼插入任務(wù)隊(duì)列,如果這個(gè)時(shí)間設(shè)為 0,就代表立即插入隊(duì)列,但不是立即執(zhí)行,仍然要等待前面代碼執(zhí)行完畢。所以 setTimeout 并不能保證執(zhí)行的時(shí)間,是否及時(shí)執(zhí)行取決于 JavaScript 線程是擁擠還是空閑。
瀏覽器的多線程機(jī)制與事件循環(huán)(event loop)
首先明確,瀏覽器的內(nèi)核是多線程的,它們在內(nèi)核制控下相互配合以保持同步,一個(gè)瀏覽器至少實(shí)現(xiàn)三個(gè)常駐線程:
javascript 引擎線程
GUI 渲染線程
瀏覽器事件觸發(fā)線程
JavaScript 引擎是單線程運(yùn)行的,瀏覽器無論在什么時(shí)候都只且只有一個(gè)線程在運(yùn)行JavaScript程序
javascript 引擎是基于事件驅(qū)動(dòng)單線程執(zhí)行的,JS引擎一直等待著任務(wù)隊(duì)列中任務(wù)的到來,然后加以處理,瀏覽器無論什么時(shí)候都只有一個(gè)JS線程在運(yùn)行JS程序。
GUI渲染線程負(fù)責(zé)渲染瀏覽器界面,當(dāng)界面需要重繪(Repaint)或由于某種操作引發(fā)回流(reflow)時(shí),該線程就會(huì)執(zhí)行。但需要注意 GUI渲染線程與JS引擎是互斥的,當(dāng)JS引擎執(zhí)行時(shí)GUI線程會(huì)被掛起,GUI更新會(huì)被保存在一個(gè)隊(duì)列中等到JS引擎空閑時(shí)立即被執(zhí)行。
事件觸發(fā)線程,當(dāng)一個(gè)事件被觸發(fā)時(shí)該線程會(huì)把事件添加到待處理隊(duì)列的隊(duì)尾,等待JS引擎的處理。這些事件可來自 JavaScript 引擎當(dāng)前執(zhí)行的代碼塊如 setTimeOut,也可來自瀏覽器內(nèi)核的其他線程如鼠標(biāo)點(diǎn)擊、AJAX 異步請求等,但由于JS的單線程關(guān)系所有這些事件都得排隊(duì)等待JS引擎處理。(當(dāng)線程中沒有執(zhí)行任何同步代碼的前提下才會(huì)執(zhí)行異步代碼)
事件循環(huán)(event loop): 是用來管理我們的異步代碼的,它會(huì)把它們放在一個(gè)線程池當(dāng)中
JavaScript中setTimeout的實(shí)現(xiàn)原理首先明確,setTimeout函數(shù)是異步代碼,但其實(shí)setTimeout并不是真正的異步操作
由于JS線程的工作機(jī)制:當(dāng)線程中沒有執(zhí)行任何同步代碼的前提下才會(huì)執(zhí)行異步代碼,setTimeout是異步代碼,所以setTimeout只能等js空閑才會(huì)執(zhí)行
前面提到過,如果代碼中設(shè)定了一個(gè) setTimeout,那么瀏覽器便會(huì)在合適的時(shí)間,將代碼插入任務(wù)隊(duì)列,如果這個(gè)時(shí)間設(shè)為 0,就代表立即插入隊(duì)列,但不是立即執(zhí)行,仍然要等待前面代碼執(zhí)行完畢。所以 setTimeout 并不能保證執(zhí)行的時(shí)間,是否及時(shí)執(zhí)行取決于 JavaScript 線程是擁擠還是空閑。
也就是說setTimeout只能保證在指定的時(shí)間過后將任務(wù)(需要執(zhí)行的函數(shù))插入隊(duì)列等候,并不保證這個(gè)任務(wù)在什么時(shí)候執(zhí)行。執(zhí)行javascript的線程會(huì)在空閑的時(shí)候,自行從隊(duì)列中取出任務(wù)然后執(zhí)行它。javascript 通過這種隊(duì)列機(jī)制,給我們制造一個(gè)異步執(zhí)行的假象。
有時(shí)setTimeout中的代碼會(huì)很快得到執(zhí)行,我們會(huì)感覺這段代碼是在異步執(zhí)行,這是因?yàn)?javascript 線程并沒有因?yàn)槭裁春臅r(shí)操作而阻塞,所以可以很快地取出排隊(duì)隊(duì)列中的任務(wù)然后執(zhí)行它。
實(shí)例分析在具備了上述理論基礎(chǔ)之后,我們對以下幾個(gè)實(shí)例進(jìn)行分析:
===========================================
var t = true; window.setTimeout(function (){ t = false; },1000); while (t){} alert("end");
運(yùn)行結(jié)果:程序陷入死循環(huán),t = false 得不到執(zhí)行,因此 alert("end") 不會(huì)執(zhí)行。
解析:
JS是單線程的,所以會(huì)先執(zhí)行 while(t){} 再 alert,但這個(gè)循環(huán)體是死循環(huán),所以永遠(yuǎn)不會(huì)執(zhí)行alert。
為什么不執(zhí)行 setTimeout?是因?yàn)镴S的工作機(jī)制是:當(dāng)線程中沒有執(zhí)行任何同步代碼的前提下才會(huì)執(zhí)行異步代碼,setTimeout是異步代碼,所以 setTimeout 只能等JS空閑才會(huì)執(zhí)行,但死循環(huán)是永遠(yuǎn)不會(huì)空閑的,所以 setTimeout 也永遠(yuǎn)得不到執(zhí)行。
===========================================
var start = new Date(); setTimeout(function(){ var end = new Date(); console.log("Time elapsed: ", end - start, "ms"); }, 500); while (new Date - start <= 1000){}
運(yùn)行結(jié)果:"Time elapsed: 1035 ms" (這里的1035不準(zhǔn)確 但是一定是大于1000的)
解析:
JS是單線程 setTimeout 異步代碼 其回調(diào)函數(shù)執(zhí)行必須需等待主線程運(yùn)行完畢
當(dāng)while循環(huán)因?yàn)闀r(shí)間差超過 1000ms 跳出循環(huán)后,setTimeout 函數(shù)中的回調(diào)才得以執(zhí)行
===========================================
for(var i=0;i<10;i++){ setTimeout(function() { console.log(i); }, 0); }
運(yùn)行結(jié)果:輸出10個(gè)10
解析:JS單線程 setTimeout 異步代碼 任務(wù)隊(duì)列
問:如何修改可以使上述代碼輸出 0123456789
自執(zhí)行函數(shù) 或 使用ES6中的let關(guān)鍵字
// 自執(zhí)行函數(shù) 形成閉包 記憶其被創(chuàng)建時(shí)的環(huán)境 for(var i=0;i<10;i++){ setTimeout((function() { console.log(i); })(), 0); }setTimeout(0)函數(shù)的作用
現(xiàn)在我們了解了setTimeout函數(shù)執(zhí)行的原理,那么它有什么作用呢?
setTimeout函數(shù)增加了Javascript函數(shù)調(diào)用的靈活性,為函數(shù)執(zhí)行順序的調(diào)度提供極大便利。
簡言之,改變順序,這正是setTimeout(0)的作用。
使用場景示例:
這里綁定了 keydown 事件,意圖是當(dāng)用戶在文本框里輸入字符時(shí),將輸入的內(nèi)容實(shí)時(shí)地在
修改代碼:
這段代碼使用setTimeout(0)就可以實(shí)現(xiàn)需要的效果了。
這里其實(shí)涉及2個(gè)任務(wù),1個(gè)是將鍵盤輸入的字符回寫到輸入框中,一個(gè)是獲取文本框的值將其寫入div中。第一個(gè)是瀏覽器自身的默認(rèn)行為,一個(gè)是我們自己編寫的代碼。很顯然,必須要先讓瀏覽器將字符回寫到文本框,然后我們才能獲取其內(nèi)容寫到div中。改變順序,這正是setTimeout(0)的作用。
其他應(yīng)用場景:有時(shí)候,加載一些廣告的時(shí)候,我們用setTimeout實(shí)現(xiàn)異步,好讓廣告不會(huì)阻塞我們頁面的渲染。
setTimeout 和 setInterval 在執(zhí)行異步代碼的時(shí)候有著根本的不同如果一個(gè)計(jì)時(shí)器被阻塞而不能立即執(zhí)行,它將延遲執(zhí)行直到下一次可能執(zhí)行的時(shí)間點(diǎn)才被執(zhí)行(比期望的時(shí)間間隔要長些)
如果setInterval回調(diào)函數(shù)的執(zhí)行時(shí)間將足夠長(比指定的時(shí)間間隔長),它們將連續(xù)執(zhí)行并且彼此之間沒有時(shí)間間隔。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/103023.html
摘要:單線程什么是單線程語言的執(zhí)行環(huán)境是單線程所謂單線程,就是指一次只能完成一件任務(wù)。如果有多個(gè)任務(wù),就必須排隊(duì),前面一個(gè)任務(wù)完成,再執(zhí)行后面一個(gè)任務(wù),以此類推。 單線程 什么是單線程? Javascript語言的執(zhí)行環(huán)境是單線程(single thread) 所謂單線程,就是指一次只能完成一件任務(wù)。 如果有多個(gè)任務(wù),就必須排隊(duì),前面一個(gè)任務(wù)完成,再執(zhí)行后面一個(gè)任務(wù),以此類推。 執(zhí)行JS代...
摘要:定時(shí)器線程由于是單線程運(yùn)行,所以不能抽出時(shí)間來計(jì)時(shí),只能另開辟一個(gè)線程來處理定時(shí)器任務(wù),等計(jì)時(shí)完成,把定時(shí)器要執(zhí)行的操作添加到事件任務(wù)隊(duì)列尾,等待引擎線程來處理。已經(jīng)知道了是單線程運(yùn)行的,也知道中有同步操作和異步操作。 js運(yùn)行機(jī)制 本章了解一下js的運(yùn)行原理,了解了js的運(yùn)行原理才能寫出更優(yōu)美的代碼,提高運(yùn)行效率,還能解決開發(fā)中遇到的不理解的問題。 進(jìn)程與線程 進(jìn)程是cpu資源分配的...
摘要:定時(shí)器線程由于是單線程運(yùn)行,所以不能抽出時(shí)間來計(jì)時(shí),只能另開辟一個(gè)線程來處理定時(shí)器任務(wù),等計(jì)時(shí)完成,把定時(shí)器要執(zhí)行的操作添加到事件任務(wù)隊(duì)列尾,等待引擎線程來處理。已經(jīng)知道了是單線程運(yùn)行的,也知道中有同步操作和異步操作。 js運(yùn)行機(jī)制 本章了解一下js的運(yùn)行原理,了解了js的運(yùn)行原理才能寫出更優(yōu)美的代碼,提高運(yùn)行效率,還能解決開發(fā)中遇到的不理解的問題。 進(jìn)程與線程 進(jìn)程是cpu資源分配的...
摘要:定時(shí)器線程由于是單線程運(yùn)行,所以不能抽出時(shí)間來計(jì)時(shí),只能另開辟一個(gè)線程來處理定時(shí)器任務(wù),等計(jì)時(shí)完成,把定時(shí)器要執(zhí)行的操作添加到事件任務(wù)隊(duì)列尾,等待引擎線程來處理。已經(jīng)知道了是單線程運(yùn)行的,也知道中有同步操作和異步操作。 js運(yùn)行機(jī)制 本章了解一下js的運(yùn)行原理,了解了js的運(yùn)行原理才能寫出更優(yōu)美的代碼,提高運(yùn)行效率,還能解決開發(fā)中遇到的不理解的問題。 進(jìn)程與線程 進(jìn)程是cpu資源分配的...
閱讀 1243·2021-09-26 09:46
閱讀 1593·2021-09-06 15:00
閱讀 725·2019-08-30 15:52
閱讀 1126·2019-08-29 13:10
閱讀 1288·2019-08-26 13:47
閱讀 1485·2019-08-26 13:35
閱讀 2034·2019-08-23 18:38
閱讀 733·2019-08-23 17:59