首頁  >  文章  >  web前端  >  JS中單線程與事件循環詳解

JS中單線程與事件循環詳解

小云云
小云云原創
2018-03-31 17:16:373471瀏覽

Js 是單一線程,js程式碼從上到下依序執行,例如我們寫了兩個函數,肯定是上面的函數先執行,下面的函數後執行。但是這種單執行緒有一個非常大的問題,就是遇到耗時的任務,後面的任務只能等它執行完,才能進行。例如ajax 請求,它從伺服器上獲取數據,這本來就耗時間, 如果網路再慢,就更耗時間,那麼我們只能等待返回結果,結果出來之後再向下執行,等待的過程中,用戶什麼都不能做,如果是在渲染階段,也會阻止渲染UI, 使用者只能看到空白頁面,體驗太差。

對於這個比較耗時的任務怎麼辦,js 決定把它放到一邊,先運行後面的任務,然後再回來處理這些耗時的任務。這就引入了非同步的概念,因為我們書寫程式碼的順序和它執行順序是不一致了。下面有三個函數,兩個基本函數,和一個ajax 函數(作非同步操作),我們的書寫順序是ajax -> add -> subtract, 但執行順序是add -> sbutract -> ajax;

function add(num1,num2) {    return num1 + num2;
}function subtract(num1,num2) {    return num2 - num1
}function ajax() {    var url = 'http://jsonplaceholder.typicode.com/posts/1';    var xhr = new XMLHttpRequest();
    xhr.open("GET",url); 
    xhr.onload= function () {
        console.log(xhr.responseText)
    }
    xhr.send()
}ajax();
console.log(add(3,5))
console.log(subtract(3,5))

  這又引出了另一個問題,異步的任務放到什麼地方了?它以後回來執行,那什麼時候執行?這裡其實要注意一個問題,真正執行ajax操作的不是我們的js,而是瀏覽器,是我們的瀏覽器發出的http 請求。當js 執行到ajax 函數的時候,它其實是告訴瀏覽器去執行http 請求,然後就立即返回,執行它後面的程式碼,就是add 和substract  函數。那麼瀏覽器執行完http請求,就從伺服器拿到了數據,怎麼辦?它這時執行我們的回呼函數(onload 函數),就是把傳回的資料傳遞給回呼函數,然後把回呼函數插入到事件(任務)佇列中。 js運行完add 和subtract 函數時,就沒有事情可以做了,它就會去輪詢事件(任務)隊列,剛一輪詢,它就發現,有一個onload 回調函數,那麼它就會把它取出來執行這個函數。在程式運行過程中,js會一直執行輪詢,直到程式結束。

  Js 程式碼在整個程式的運行過程中分成兩個部分,一種是像add 函數這種,另一種是像ajax onload 回呼函數這種,相對應js 運行也分成了兩個部分,一個是主線程,一個是任務(非同步回呼函數)佇列。

  js程式碼具體運作如下:

  1, js 程式碼一載入進來,它就會從上到下依序執行,它也就進入一個全域執行環境,當它遇到ajax非同步操作的時候,它會告訴瀏覽器去執行請求,然後立即返回,執行下面的程式碼,遇到add函數調用,它就會進入函數執行環境,執行完add函數時,它又遇到subtract 函數,它又會進入函數執行環境。當然這裡比較簡單,沒有巢狀函數。如果函數中還嵌套一個函數,那麼它就會進入子函數的執行環境,像下圖一樣,這就形成了一個執行棧,上面的執行後,再執行下面的,直到全域執行環境中的程式碼執行完畢,執行棧為空, 這就是js 的主執行緒。

  2,瀏覽器去執行http 請求, 在未來的一段時間內,它或從伺服器取得資料或失敗,這時它就會把我們註冊的成功或失敗的回呼函數放入到任務(回呼函數)隊例中。

  3, 當執行堆疊中的所有程式碼執行完畢後,就是執行堆疊為空時,js就會去輪詢我們的任務佇列(左邊圖片),如果有任務,它就會從任務佇列的起始位置取出第一個任務(註冊的回呼函數)放到執行棧去執行(右邊圖),等這個回呼函數執行完畢後,執行棧再為空, js再去輪詢我們的任務佇列,如果還有回呼函數,它再取出第一個放到執行堆疊執行。在整個程式的執行過程中,js 會一直輪詢我們的任務佇列,一有任務,就會執行,這就是事件循環。

  所有的程式都是在堆疊中執行,只有執行棧空了,它有能力處理下一個任務。只要一個函數進入執行棧,棧就不為空,棧不為空,就無法處理下一個函數,只能等到函數執行完畢。這就是所謂的 “run–to-complete ” 一次只能做一件事情。這也要求我們的回呼函數中,不能執行太多任務,如果執行太多任務,堆疊就不為空,也就阻止了下一個任務的執行,造成阻塞。

 以上所述,就是js 中的非阻塞。

相關推薦:

JavaScript運行機制之為什麼JavaScript是單執行緒

單執行緒JS與多執行緒瀏覽器的使用

原生JS非同步與單執行緒詳解

以上是JS中單線程與事件循環詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn