摘要:異步任務(wù)必須指定回調(diào)函數(shù),當(dāng)主線程開始執(zhí)行異步任務(wù),就是執(zhí)行對應(yīng)的回調(diào)函數(shù)。
JavaScript語言的一大特點就是單線程,也就是說,同一個時間只能做一件事。那么,為什么JavaScript不能有多個線程呢?這樣能提高效率啊。
JavaScript的單線程,與它的用途有關(guān)。作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM。這決定了它只能是單線程,否則會帶來很復(fù)雜的同步問題。比如,假定JavaScript同時有兩個線程,一個線程在某個DOM節(jié)點上添加內(nèi)容,另一個線程刪除了這個節(jié)點,這時瀏覽器應(yīng)該以哪個線程為準(zhǔn)?
所以,為了避免復(fù)雜性,從一誕生,JavaScript就是單線程,這已經(jīng)成了這門語言的核心特征,將來也不會改變。
為了利用多核CPU的計算能力,HTML5提出Web Worker標(biāo)準(zhǔn),允許JavaScript腳本創(chuàng)建多個線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個新標(biāo)準(zhǔn)并沒有改變JavaScript單線程的本質(zhì)。
其實同步和異步,無論如何,做事情的時候都是只有一條流水線(單線程),同步和異步的差別就在于這條流水線上各個流程的執(zhí)行順序不同。
最基礎(chǔ)的異步是setTimeout和setInterval函數(shù),很常見,但是很少人有人知道其實這就是異步,因為它們可以控制js的執(zhí)行順序。我們也可以簡單地理解為:可以改變程序正常執(zhí)行順序的操作就可以看成是異步操作。
盡管我們設(shè)置了setTimeout(function,time)中的等待時間為0,結(jié)果其中的function還是后執(zhí)行。
火狐瀏覽器的api文檔有這樣一句話:Because even though setTimeout was called with a delay of zero, it"s placed on a queue and scheduled to run at the next opportunity, not immediately. Currently executing code must complete before functions on the queue are executed, the resulting execution order may not be as expected.
意思就是:盡管setTimeout的time延遲時間為0,其中的function也會被放入一個隊列中,等待下一個機(jī)會執(zhí)行,當(dāng)前的代碼(指不需要加入隊列中的程序)必須在該隊列的程序完成之前完成,因此結(jié)果可能不與預(yù)期結(jié)果相同。
這里說到了一個“隊列”(即任務(wù)隊列),該隊列放的是什么呢,放的就是setTimeout中的function,這些function依次加入該隊列,即該隊列中所有function中的程序?qū)谠撽犃幸酝獾乃写a執(zhí)行完畢之后再以此執(zhí)行,這是為什么呢?因為在執(zhí)行程序的時候,瀏覽器會默認(rèn)setTimeout以及ajax請求這一類的方法都是耗時程序(盡管可能不耗時),將其加入一個隊列中,該隊列是一個存儲耗時程序的隊列,在所有不耗時程序執(zhí)行過后,再來依次執(zhí)行該隊列中的程序。
又回到了最初的起點——javascript是單線程。單線程就意味著,所有任務(wù)需要排隊,前一個任務(wù)結(jié)束,才會執(zhí)行后一個任務(wù)。如果前一個任務(wù)耗時很長,后一個任務(wù)就不得不一直等著。于是就有一個概念——任務(wù)隊列。如果排隊是因為計算量大,CPU忙不過來,倒也算了,但是很多時候CPU是閑著的,因為IO設(shè)備(輸入輸出設(shè)備)很慢(比如Ajax操作從網(wǎng)絡(luò)讀取數(shù)據(jù)),不得不等著結(jié)果出來,再往下執(zhí)行。于是JavaScript語言的設(shè)計者意識到,這時主線程完全可以不管IO設(shè)備,掛起處于等待中的任務(wù),先運行排在后面的任務(wù)。等到IO設(shè)備返回了結(jié)果,再回過頭,把掛起的任務(wù)繼續(xù)執(zhí)行下去。
于是,所有任務(wù)可以分成兩種,一種是同步任務(wù)(synchronous),另一種是異步任務(wù)(asynchronous)。同步任務(wù)指的是,在主線程上排隊執(zhí)行的任務(wù),只有前一個任務(wù)執(zhí)行完畢,才能執(zhí)行后一個任務(wù);異步任務(wù)指的是,不進(jìn)入主線程、而進(jìn)入"任務(wù)隊列"(task queue)的任務(wù),只有等主線程任務(wù)執(zhí)行完畢,"任務(wù)隊列"開始通知主線程,請求執(zhí)行任務(wù),該任務(wù)才會進(jìn)入主線程執(zhí)行。
具體來說,異步運行機(jī)制如下:
(1)所有同步任務(wù)都在主線程上執(zhí)行,形成一個執(zhí)行棧(execution context stack)。
(2)主線程之外,還存在一個"任務(wù)隊列"(task queue)。只要異步任務(wù)有了運行結(jié)果,就在"任務(wù)隊列"之中放置一個事件。
(3)一旦"執(zhí)行棧"中的所有同步任務(wù)執(zhí)行完畢,系統(tǒng)就會讀取"任務(wù)隊列",看看里面有哪些事件。那些對應(yīng)的異步任務(wù),于是結(jié)束等待狀態(tài),進(jìn)入執(zhí)行棧,開始執(zhí)行。
(4)主線程不斷重復(fù)上面的第三步。
只要主線程空了,就會去讀取"任務(wù)隊列",這就是JavaScript的運行機(jī)制。這個過程會不斷重復(fù)。
"任務(wù)隊列"是一個事件的隊列(也可以理解成消息的隊列),IO設(shè)備完成一項任務(wù),就在"任務(wù)隊列"中添加一個事件,表示相關(guān)的異步任務(wù)可以進(jìn)入"執(zhí)行棧"了。主線程讀取"任務(wù)隊列",就是讀取里面有哪些事件。
"任務(wù)隊列"中的事件,除了IO設(shè)備的事件以外,還包括一些用戶產(chǎn)生的事件(比如鼠標(biāo)點擊、頁面滾動等等),比如$(selectot).click(function),這些都是相對耗時的操作。只要指定過這些事件的回調(diào)函數(shù),這些事件發(fā)生時就會進(jìn)入"任務(wù)隊列",等待主線程讀取。
所謂"回調(diào)函數(shù)"(callback),就是那些會被主線程掛起來的代碼,前面說的點擊事件$(selectot).click(function)中的function就是一個回調(diào)函數(shù)。異步任務(wù)必須指定回調(diào)函數(shù),當(dāng)主線程開始執(zhí)行異步任務(wù),就是執(zhí)行對應(yīng)的回調(diào)函數(shù)。例如ajax的success,complete,error也都指定了各自的回調(diào)函數(shù),這些函數(shù)就會加入“任務(wù)隊列”中,等待執(zhí)行。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/107946.html
摘要:深入理解引擎的執(zhí)行機(jī)制最近在反省,很多知識都是只會用,不理解底層的知識。在閱讀之前,請先記住兩點是單線程語言的是的執(zhí)行機(jī)制。所以,是存在異步執(zhí)行的,比如單線程是怎么實現(xiàn)異步的場景描述通過事件循環(huán),所以說,理解了機(jī)制,也就理解了的執(zhí)行機(jī)制啦。 深入理解js引擎的執(zhí)行機(jī)制 最近在反省,很多知識都是只會用,不理解底層的知識。所以在開發(fā)過程中遇到一些奇怪的比較難解決的bug,在思考的時候就會收...
摘要:圖片轉(zhuǎn)引自的演講和兩個定時器中回調(diào)的執(zhí)行邏輯便是典型的機(jī)制。異步編程關(guān)于異步編程我的理解是,在執(zhí)行環(huán)境所提供的異步機(jī)制之上,在應(yīng)用編碼層面上實現(xiàn)整體流程控制的異步風(fēng)格。 問題背景 在一次開發(fā)任務(wù)中,需要實現(xiàn)如下一個餅狀圖動畫,基于canvas進(jìn)行繪圖,但由于對于JS運行環(huán)境中異步機(jī)制的不了解,所以遇到了一個棘手的問題,始終無法解決,之后在與同事交流之后才恍然大悟。問題的根節(jié)在于經(jīng)典的J...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個符合規(guī)范并可配合使用的寫一個符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個需求:在系統(tǒng)初始化時通過http獲取一個第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個接口,可通過...
摘要:當(dāng)引擎開始執(zhí)行一個函數(shù)比如回調(diào)函數(shù)時,它就會把這個函數(shù)執(zhí)行完,也就是說只有執(zhí)行完這段代碼才會繼續(xù)執(zhí)行后面的代碼。當(dāng)條件允許時,回調(diào)函數(shù)就會被運行。現(xiàn)在,返回去執(zhí)行注冊的那個回調(diào)函數(shù)。 原文地址:http://blog.getify.com/promis... 在微博上看到有人分享LabJS作者寫的關(guān)于Promise的博客,看了下覺得寫得很好,分五個部分講解了Promise的來龍去脈。從...
閱讀 2041·2021-11-24 09:39
閱讀 1902·2019-08-30 15:55
閱讀 2195·2019-08-30 15:53
閱讀 620·2019-08-29 13:16
閱讀 1009·2019-08-26 12:20
閱讀 2412·2019-08-26 11:58
閱讀 3176·2019-08-26 10:19
閱讀 3339·2019-08-23 18:31