關於js中的同步和異步


最近看到前端面試問到js中的同步和異步,這個問題該怎么回答?

梳理一下,js對於異步的處理,很多人的第一反應是ajax,這只能說是對了一半。

 

1.個人覺得,js中,最基礎的異步是setTimeout和setInterval函數,很常見,但是很少人有人知道其實這就是異步,因為它們可以控制js的執行順序

var timeoutID = window.setTimeout(func[, delay, param1, param2, ...]);

看看下面的測試代碼會出現什么?? 要求出現的順序是張一

    <script type="text/javascript">
        console.log( "張一" );
        setTimeout(function() {
            console.log( "王三" )
        }, 500 );
        setTimeout(function() {
            console.log( "李四" )
        }, 500 );
        setTimeout(function() {
            console.log( "劉五" )
        }, 500 );
        console.log( "李二" );
    </script>

結果正如設計的:

但是 查看 火狐的api文檔 有這樣一句話  api地址 https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout

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調用延遲為零,這是放置在一個隊列和調度運行到下一個機會,沒有立即。當前正在執行的代碼必須在隊列上的函數執行之前完成,由此產生的執行順序可能不會像預期的那樣。

所以,可預料的結果是:直到在同一程序段中所有其余的代碼執行結束后,超時才會發生。所以如果設置了超時,同時執行了需長時間運行的函數,那么在該函數執行完成之前,超時甚至都不會啟動。實際上,異步函數,如setTimeout和setInterval,被壓入了稱之為Event Loop的隊列。有點扯遠,記住就是setTimeout和setInterval可以改變一個隊列函數的執行順序。

 

2.ajax異步

很多時候,我們使用的ajax都是jQuery封裝后的.ajax() 如:api中的ajax格式。

        $.ajax({
            type: "POST",
            url: "some.php",
            data: "name=John&location=Boston",
            success: function(msg){
                alert( "Data Saved: " + msg );
            }
        });

很多人的理解是 是在調用$.ajax之后馬上使用data?  但是真的是這樣嗎?

如果你寫過原生的ajax,應該會知道如下:https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Types_of_requests

function reqListener () {
  console.log(this.responseText);
}

var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send();

這個火狐官方的例子很好的說明我們的ajax執行的步驟,XmlHttpRequest對象發起請求,設置回調函數用來處理XHR的readystatechnage事件。然后執行XHR的send方法。在XHR運行中,當其屬性readyState改變時readystatechange事件就會被觸發,只有在XHR從遠端服務器接收響應結束時回調函數才會觸發執行。也就是如下:

xmlhttp.open( "GET", "url", true );
        xmlhttp.onreadystatechange = function( data ) {
            if ( xmlhttp.readyState === 4 ) {
                console.log( data );
            }
        };
        xmlhttp.send( null );

 

以上兩點就是JavaScript中的同步和異步。

 

延伸 摘自阮一峰es6入門:

於是,這個問題又回到了最開始的起點:JavaScript是單線程的。

 

JavaScript語言的一大特點就是單線程,也就是說,同一個時間只能做一件事。那么,為什么JavaScript不能有多個線程呢?這樣能提高效率啊。

JavaScript的單線程,與它的用途有關。作為瀏覽器腳本語言,JavaScript的主要用途是與用戶互動,以及操作DOM。這決定了它只能是單線程,否則會帶來很復雜的同步問題。比如,假定JavaScript同時有兩個線程,一個線程在某個DOM節點上添加內容,另一個線程刪除了這個節點,這時瀏覽器應該以哪個線程為准?

所以,為了避免復雜性,從一誕生,JavaScript就是單線程,這已經成了這門語言的核心特征,將來也不會改變。

為了利用多核CPU的計算能力,HTML5提出Web Worker標准,允許JavaScript腳本創建多個線程,但是子線程完全受主線程控制,且不得操作DOM。所以,這個新標准並沒有改變JavaScript單線程的本質。

單線程就意味着,所有任務需要排隊,前一個任務結束,才會執行后一個任務。如果前一個任務耗時很長,后一個任務就不得不一直等着。於是就有一個概念,任務隊列。

如果排隊是因為計算量大,CPU忙不過來,倒也算了,但是很多時候CPU是閑着的,因為IO設備(輸入輸出設備)很慢(比如Ajax操作從網絡讀取數據),不得不等着結果出來,再往下執行。

JavaScript語言的設計者意識到,這時主線程完全可以不管IO設備,掛起處於等待中的任務,先運行排在后面的任務。等到IO設備返回了結果,再回過頭,把掛起的任務繼續執行下去。

於是,所有任務可以分成兩種,一種是同步任務(synchronous),另一種是異步任務(asynchronous)。同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行后一個任務;異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務可以執行了,該任務才會進入主線程執行。

具體來說,異步執行的運行機制如下。(同步執行也是如此,因為它可以被視為沒有異步任務的異步執行。)

(1)所有同步任務都在主線程上執行,形成一個執行棧(execution context stack)。

(2)主線程之外,還存在一個"任務隊列"(task queue)。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。

(3)一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務隊列",看看里面有哪些事件。那些對應的異步任務,於是結束等待狀態,進入執行棧,開始執行。

(4)主線程不斷重復上面的第三步。

下圖就是主線程和任務隊列的示意圖。

任務隊列

只要主線程空了,就會去讀取"任務隊列",這就是JavaScript的運行機制。這個過程會不斷重復。  

 

"任務隊列"是一個事件的隊列(也可以理解成消息的隊列),IO設備完成一項任務,就在"任務隊列"中添加一個事件,表示相關的異步任務可以進入"執行棧"了。主線程讀取"任務隊列",就是讀取里面有哪些事件。

"任務隊列"中的事件,除了IO設備的事件以外,還包括一些用戶產生的事件(比如鼠標點擊、頁面滾動等等)。只要指定過回調函數,這些事件發生時就會進入"任務隊列",等待主線程讀取。

所謂"回調函數"(callback),就是那些會被主線程掛起來的代碼。異步任務必須指定回調函數,當主線程開始執行異步任務,就是執行對應的回調函數。

"任務隊列"是一個先進先出的數據結構,排在前面的事件,優先被主線程讀取。主線程的讀取過程基本上是自動的,只要執行棧一清空,"任務隊列"上第一位的事件就自動進入主線程。但是,由於存在后文提到的"定時器"功能,主線程首先要檢查一下執行時間,某些事件只有到了規定的時間,才能返回主線程。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM