本篇文章給大家分享的內容是關於js中的同步和異步的理解,有著一定的參考價值,有需要的朋友可以參考一下
你應該知道,javascript語言是一門「單線程」的語言,不像java語言,類別繼承Thread再來個thread.start就可以開闢一個線程,所以,javascript就像一條管線,只是一條流水線而已,要嘛加工,要嘛包裝,不能同時進行多個任務和流程。
那麼這裡說的同步和異步到底是什麼呢?如果你真的不懂,我希望你認真讀完這篇文章。其實我個人覺得js官方的文件在使用兩個字的時候並不準確,包含很多其他詞彙,都只是聽起來高深,但實際應用好像跟這些字沒半毛錢關係。例如「事件委託」這個詞,不知道的人乍看誰又能說出「事件委託」是什麼意思?委託什麼事件?怎麼個委託,我看不如乾脆就叫“事件在外層元素中的捕獲”,雖然長一點,一下就能看懂。
回歸正軌,「同步」——一下就讓人想到「一起」這個詞;「異步」呢,從字面來講,好像是在不同的(異)的ways上do something,那首先想到的詞可能是“一邊...一邊...”,比如'小明一邊吃雪糕一邊寫作業',這完全沒毛病,雪糕吃完了,作業也寫完了,這就是異步?那就大錯特錯了!
其實同步且非同步,無論如何,做事情的時候都是只有一條管線(單執行緒),同步與非同步的差別就在於這條管線上各個流程的執行順序不同。
最基礎的非同步是setTimeout和setInterval函數,很常見,但很少人知道其實這就是非同步,因為它們可以控制js的執行順序。我們也可以簡單地理解為:可以改變程式正常執行順序的操作就可以看成是非同步操作。如下程式碼:
<script type="text/javascript"> console.log( "1" ); setTimeout(function() { console.log( "2" ) }, 0 ); setTimeout(function() { console.log( "3" ) }, 0 ); setTimeout(function() { console.log( "4" ) }, 0 ); console.log( "5" ); </script>
輸出順序是什麼呢?
可見,儘管我們設定了setTimeout(function,time)中的等待時間為0,結果其中的function還是後執行。
火狐瀏覽器的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 resplete before functions.
下一個機會執行,目前的程式碼(指不需要加入佇列中的程式)必須在該佇列的程式完成之前完成,因此結果可能不與預期結果相同。
這裡說到了一個「佇列」(即任務佇列),該佇列放的是什麼呢,放的就是setTimeout中的function,這些function依序加入該佇列,即該佇列中所有function中的程式將會在該佇列以外的所有程式碼執行完畢之後再以此執行,這是為什麼呢?因為在執行程式的時候,瀏覽器會預設setTimeout以及ajax請求這一類的方法都是耗時程式(儘管可能不耗時),將其加入一個佇列中,該佇列是一個儲存耗時程式的佇列,在所有不耗時程式執行過後,再來依序執行該佇列中的程式。 又回到了最初的起點-javascript是單執行緒。單線程就意味著,所有任務都需要排隊,前一個任務結束,才會執行後一個任務。如果前一個任務耗時很長,後一個任務就得一直等著。於是就有一個概念──任務隊列。如果排隊是因為運算量大,CPU忙不過來,倒也算了,但是很多時候CPU是閒著的,因為IO設備(輸入輸出設備)很慢(例如Ajax操作從網路讀取資料),不得不等著結果出來,再往下執行。於是JavaScript語言的設計者意識到,這時主執行緒完全可以不管IO設備,掛起處於等待中的任務,先執行排在後面的任務。等到IO設備回傳了結果,再回過頭,把掛起的任務繼續執行下去。於是,所有任務可以分成兩種,一種是同步任務(synchronous),另一種是非同步任務(asynchronous)。同步任務指的是,在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;非同步任務指的是,不進入主執行緒、而進入"任務佇列"(task queue)的任務,只有等主線程任務執行完畢,"任務隊列"開始通知主線程,請求執行任務,該任務才會進入主線程執行。
具體來說,非同步運行機制如下:
(1)所有同步任務都在主執行緒上執行,形成一個執行堆疊(execution context stack)。
(2)主執行緒之外,還存在一個"任務佇列"(task queue)。只要非同步任務有了運行結果,就在"任務隊列"之中放置一個事件。
(3)一旦"執行堆疊"中的所有同步任務執行完畢,系統就會讀取"任務佇列",看看裡面有哪些事件。那些對應的非同步任務,於是結束等待狀態,進入執行棧,開始執行。
(4)主執行緒不斷重複上面的第三步。
只要主執行緒空了,就會去讀取"任務佇列",這就是JavaScript的運作機制。這個過程會不斷重複。
"任務佇列"是一個事件的佇列(也可以理解成訊息的佇列),IO裝置完成一項任務,就在"任務佇列"中新增一個事件,表示相關的非同步任務可以進入"執行棧"了。主執行緒讀取"任務佇列",就是讀取裡面有哪些事件。
"任務佇列"中的事件,除了IO設備的事件以外,還包括一些使用者產生的事件(例如滑鼠點擊、頁面滾動等等),例如$(selectot).click(function),這些都是相對耗時的操作。只要指定過這些事件的回呼函數,這些事件發生時就會進入"任務佇列",等待主執行緒讀取。
所謂"回呼函數"(callback),就是那些會被主執行緒掛起來的程式碼,前面說的點擊事件$(selectot).click(function)中的function就是一個回呼函數。非同步任務必須指定回呼函數,當主執行緒開始執行非同步任務,就是執行對應的回呼函數。例如ajax的success,complete,error也都指定了各自的回呼函數,這些函數就會加入「任務佇列」中,等待執行。
相關推薦:
以上是關於js中的同步和非同步的理解的詳細內容。更多資訊請關注PHP中文網其他相關文章!