摘要:而之后事件循環(huán)一直會去遍歷任務(wù)隊列,一旦有任務(wù)放入就會放入主線程中執(zhí)行。任務(wù)隊列所謂任務(wù)是返回的一個個通知,讓主線程在讀取任務(wù)隊列的時候得知這個異步任務(wù)已經(jīng)完成,下一步該執(zhí)行這個任務(wù)的回調(diào)函數(shù)了。
javascript單線程
瀏覽器端,復(fù)雜的UI環(huán)境會限制多線程語言的開發(fā)。棧
例如,一個線程在操作一個DOM元素時,另一個線程需要去刪除DOM元素,
這個之間就需要進(jìn)行狀態(tài)的同步,何況前端可能不止操作這么一個DOM元素。
所以,為了避免在開發(fā)過程中,去進(jìn)行復(fù)雜的同步,選用單線程語言進(jìn)行開發(fā)是最好的解決方案
棧就是和列表類似的一種數(shù)據(jù)結(jié)構(gòu),它可以用來解決計算機(jī)世界里很多的問題。棧是一種高效的數(shù)據(jù)結(jié)構(gòu),因為數(shù)據(jù)只能在棧頂添加或刪除,所以這樣的操作很快,而且容易實現(xiàn)。
棧的使用遍布程序語言的方方面面,從表達(dá)式求值到處理函數(shù)調(diào)用; 函數(shù)調(diào)用形成了一個棧幀
具有一種后入先出(LIFO,last-in-first-out)的數(shù)據(jù)結(jié)構(gòu)
由于棧具有后入先出的特點,所有任何不在棧頂?shù)脑囟紵o法訪問,為了拿到棧底的元素,必選先拿掉上面的元素
可在線演示這段代碼的執(zhí)行流程
function fun1(){ return "hello hip-hop"; } function fun2(){ return fun1(); } function fun3(){ console.log(fun2()); } fun3(); //"hello hip-hop"堆(引用類型)
當(dāng)我們在程序中創(chuàng)建一個對象時,這個對象將被保存到運行時數(shù)據(jù)區(qū)中,以便反復(fù)利用(因為對象的創(chuàng)建成本開銷較大),這個運行時數(shù)據(jù)區(qū)就是堆內(nèi)存。隊列
堆內(nèi)存中的對象不會隨方法的結(jié)束而銷毀,即使方法結(jié)束后,這個對象還可能被另一個引用變量所引用(方法的參數(shù)傳遞時很常見),則這個對象依然不會被銷毀,只有當(dāng)一個對象沒有任何引用變量引用它時,
系統(tǒng)的垃圾回收機(jī)制才會在核實的時候回收它對象被分配在一個堆中,即用以表示一個大部分非結(jié)構(gòu)化的內(nèi)存區(qū)域
排隊在第一個的人先辦理業(yè)務(wù),其它人只能排著,直到輪到他們?yōu)橹怪荒茉谀┪膊迦朐兀陉犑讋h除元素。事件循環(huán)機(jī)制
隊列用于存儲按順序排列的數(shù)據(jù)先進(jìn)先出列被用在很多地方。比如提交操作系統(tǒng)執(zhí)行一系列進(jìn)程。打印任務(wù)池等。一些仿真系統(tǒng)用來模擬銀行或雜貨店里排隊的顧客
這幅圖片中,我們可以看到完整的執(zhí)行流程,其中涉及到的異步事件有DOM事件、ajax請求和setTimeout。所以,整體的執(zhí)行流程是這樣子的:
所有同步任務(wù)會在主線程的調(diào)用棧中執(zhí)行。
在主線程之外,還有一個任務(wù)隊列,一旦指定事件發(fā)生之后,異步任務(wù)就會被放入任務(wù)隊列中
當(dāng)主線程執(zhí)行完調(diào)用棧中的同步任務(wù)時,會遍歷任務(wù)隊列,將任務(wù)隊列中的任務(wù)放入主線程中執(zhí)行。而之后事件循環(huán)一直會去遍歷任務(wù)隊列,一旦有任務(wù)放入就會放入主線程中執(zhí)行。
這樣,我們就已經(jīng)初步了解了同步和異步之間的實現(xiàn),以及瀏覽器中的事件循環(huán)機(jī)制。
任務(wù)隊列所謂任務(wù)是WebAPIs返回的一個個通知,讓JS主線程在讀取任務(wù)隊列的時候得知這個異步任務(wù)已經(jīng)完成,下一步該執(zhí)行這個任務(wù)的回調(diào)函數(shù)了。
主線程擁有多個任務(wù)隊列,不同的任務(wù)隊列用來排列來自不同任務(wù)源的任務(wù)。
任務(wù)源是什么?像setTimeout/Promise/DOM事件等都是任務(wù)源,來自同類任務(wù)源的任務(wù)我們稱它們是同源的,比如setTimeout與setInterval就是同源的。
在ES6標(biāo)準(zhǔn)中任務(wù)隊列又分為宏觀任務(wù)隊列和微觀任務(wù)隊列
ES6標(biāo)準(zhǔn)中任務(wù)隊列存在兩種類型,一種就是上邊提到的一些隊列,
宏觀任務(wù)隊列(macrotask queue):setTimeout、網(wǎng)絡(luò)請求Ajax、用戶IO等,
微觀任務(wù)隊列(microtask queue),Promise就屬于微觀任務(wù)隊列
在執(zhí)行棧執(zhí)行的過程中會把屬于微觀任務(wù)隊列的任務(wù)分配到相應(yīng)的微觀任務(wù)隊列中去。而在調(diào)用棧執(zhí)行空之后,主線程讀取任務(wù)隊列時,會先讀取所有微觀任務(wù)隊列,然后讀取一個宏觀任務(wù)隊列,再讀取所有的微觀任務(wù)隊列
setTimeout(function(){console.log(4)},0); new Promise(function(resolve){ console.log(1) for( var i=0 ; i<10000 ; i++ ){ i==9999 && resolve() } console.log(2) }).then(function(){ console.log(5) }); console.log(3);
腳本開始執(zhí)行,最先遇到setTimeout,交給瀏覽器去計時,達(dá)到setTimeout限制最短計時之后,把這個任務(wù)推入setTimeout隊列。
遇到Promise構(gòu)造函數(shù),構(gòu)造函數(shù)參數(shù)執(zhí)行,輸出1,調(diào)用resolve改變Promise對象的狀態(tài),然后輸出2。
Promise對象調(diào)用then方法,將這個任務(wù)推入Promise任務(wù)隊列。
執(zhí)行console.log(3),輸出3。
調(diào)用棧為空,讀取任務(wù)隊列,按照 讀取所有微觀任務(wù)隊列 -> 執(zhí)行 ->
讀取一個宏觀任務(wù)隊列 -> 執(zhí)行 ->
讀取所有微觀任務(wù)隊列 -> 執(zhí)行 ->
再讀取一個宏觀任務(wù)隊列…的順序。
讀取所有微觀任務(wù)隊列中的任務(wù),執(zhí)行這些任務(wù)指定的回調(diào)函數(shù)。執(zhí)行then指定的回調(diào)函數(shù),輸出5(微觀任務(wù)隊列也具有優(yōu)先級)。
最后讀取到setTimeout的任務(wù),執(zhí)行回調(diào)函數(shù),輸出4。
所以最后的輸出順序是1,2,3,5,4,而不是1,2,3,4,5。如果不清楚微觀任務(wù)隊列的執(zhí)行機(jī)制,很容易將兩個異步任務(wù)歸為一類,將執(zhí)行順序判斷錯誤
零延遲零延遲并不是意味著回調(diào)會立即執(zhí)行。 在零延遲調(diào)用 setTimeout 時,其并不是過了給定的時間間隔后就馬上執(zhí)行回調(diào)函數(shù)。
其等待的時間基于隊列里正在等待的消息數(shù)量。 在下面的例子中,"this is just a message" 將會在回調(diào)
(callback) 獲得處理之前輸出到控制臺, 這是因為延遲是要求運行時 (runtime) 處理請求所需的最小時間,但不是有所保證的時間
(function () { console.log("this is the start"); setTimeout(function cb() { console.log("this is a msg from call back"); }); console.log("this is just a message"); setTimeout(function cb1() { console.log("this is a msg from call back1"); }, 0); console.log("this is the end"); })(); // "this is the start" // "this is just a message" // "this is the end" // "this is a msg from call back" // "this is a msg from call back1"
參考
《javascript高級程序設(shè)計》
https://developer.mozilla.org...
http://www.cnblogs.com/ahthw/...
https://kongchenglc.github.io...
https://github.com/laizimo/zi...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/110125.html
摘要:閉包能用來實現(xiàn)私有化和創(chuàng)建工廠函數(shù)等作用。關(guān)于閉包的常見面試題是這樣的寫一個函數(shù),循環(huán)一個整數(shù)數(shù)組,延遲秒打印這個數(shù)組中每個元素的索引。 文章來源:http://mp.weixin.qq.com/s/vs0... 前言 在公眾號上看到了這篇文章,覺得很有用,有助于理解JS學(xué)習(xí)中的一些重點難點。決定把它整理下發(fā)布出來。該文章主要介紹了JS中的三個問題。在以后的幾篇文章里,我會詳細(xì)介紹這三...
摘要:支持三個參數(shù)分別表示事件名稱,是否可以冒泡,是否阻止事件的默認(rèn)操作觸發(fā)參數(shù)表示事件對象,是方法返回的創(chuàng)建的對象監(jiān)聽方法自定義事件常用模擬模擬方法操作或者自定義事件我的自定義事件。 事件這塊知識點雖然是老生長談的,但對于我來說多多整理,多多感悟,溫故知新,每次看看這塊都有不同的收獲.(在這里我不會長篇大論,只會挑重點;具體的小伙伴們自行查找) 什么是事件 在編程時系統(tǒng)內(nèi)發(fā)生的動作或者發(fā)生...
摘要:支持三個參數(shù)分別表示事件名稱,是否可以冒泡,是否阻止事件的默認(rèn)操作觸發(fā)參數(shù)表示事件對象,是方法返回的創(chuàng)建的對象監(jiān)聽方法自定義事件常用模擬模擬方法操作或者自定義事件我的自定義事件。 事件這塊知識點雖然是老生長談的,但對于我來說多多整理,多多感悟,溫故知新,每次看看這塊都有不同的收獲.(在這里我不會長篇大論,只會挑重點;具體的小伙伴們自行查找) 什么是事件 在編程時系統(tǒng)內(nèi)發(fā)生的動作或者發(fā)生...
摘要:支持三個參數(shù)分別表示事件名稱,是否可以冒泡,是否阻止事件的默認(rèn)操作觸發(fā)參數(shù)表示事件對象,是方法返回的創(chuàng)建的對象監(jiān)聽方法自定義事件常用模擬模擬方法操作或者自定義事件我的自定義事件。 事件這塊知識點雖然是老生長談的,但對于我來說多多整理,多多感悟,溫故知新,每次看看這塊都有不同的收獲.(在這里我不會長篇大論,只會挑重點;具體的小伙伴們自行查找) 什么是事件 在編程時系統(tǒng)內(nèi)發(fā)生的動作或者發(fā)生...
閱讀 1951·2021-09-24 09:48
閱讀 3285·2021-08-26 14:14
閱讀 1777·2021-08-20 09:36
閱讀 1541·2019-08-30 15:55
閱讀 3686·2019-08-26 17:15
閱讀 1513·2019-08-26 12:09
閱讀 676·2019-08-26 11:59
閱讀 3384·2019-08-26 11:57