javascript
javascript 等待指定时间_javascript的单线程和任务队列
一、JavaScript為什么設計為單線程?
JavaScript語言的一大特點就是單線程,換言之就是同一個時間只能做一件事。其他任務都必須在后面排隊等待。
for(var i = 0; i < 5; i++) {console.log(i); } console.log('end');上面的代碼,只有for循環執行完畢,才會執行end;
JavaScript 之所以采用單線程,而不是多線程,跟歷史有關系。JavaScript 從誕生起就是單線程,原因是不想讓瀏覽器變得太復雜,因為多線程需要共享資源、且有可能修改彼此的運行結果,對于一種網頁腳本語言來說,這就太復雜了。如果 JavaScript 同時有兩個線程,一個線程在網頁 DOM 節點上添加內容,另一個線程刪除了這個節點,這時瀏覽器應該以哪個線程為準?是不是還要有鎖機制?所以,為了避免復雜性,JavaScript 一開始就是單線程,這已經成了這門語言的核心特征,將來也不會改變。
這種模式的好處是實現起來比較簡單,執行環境相對單純;壞處是只要有一個任務耗時很長,后面的任務都必須排隊等著,會拖延整個程序的執行。常見的瀏覽器無響應(假死),往往就是因為某一段 JavaScript 代碼長時間運行(比如死循環),導致整個頁面卡在這個地方,其他任務無法執行。JavaScript 語言本身并不慢,慢的是讀寫外部數據,比如等待 Ajax 請求返回結果。這個時候,如果對方服務器遲遲沒有響應,或者網絡不通暢,就會導致腳本的長時間停滯。
如果排隊是因為計算量大,CPU 忙不過來,倒也算了,但是很多時候 CPU 是閑著的,因為 IO 操作(輸入輸出)很慢(比如 Ajax 操作從網絡讀取數據),不得不等著結果出來,再往下執行。JavaScript 語言的設計者意識到,這時 CPU 完全可以不管 IO 操作,掛起處于等待中的任務,先運行排在后面的任務。等到 IO 操作返回了結果,再回過頭,把掛起的任務繼續執行下去。這種機制就是 JavaScript 內部采用的“事件循環”機制(Event Loop)。
單線程模型雖然對 JavaScript 構成了很大的限制,但也因此使它具備了其他語言不具備的優勢。如果用得好,JavaScript 程序是不會出現堵塞的,這就是為什么 Node 可以用很少的資源,應付大流量訪問的原因。
為了利用多核 CPU 的計算能力,HTML5 提出 Web Worker 標準,允許 JavaScript 腳本創建多個線程,但是子線程完全受主線程控制,且不得操作 DOM。所以,這個新標準并沒有改變 JavaScript 單線程的本質。二、同步任務和異步任務
程序里面所有的任務,可以分成兩類:同步任務(synchronous)和異步任務(asynchronous)。
同步任務是那些沒有被引擎掛起、在主線程上排隊執行的任務。只有前一個任務執行完畢,才能執行后一個任務。
異步任務是那些被引擎放在一邊,不進入主線程、而進入任務隊列的任務。只有引擎認為某個異步任務可以執行了(比如 Ajax 操作從服務器得到了結果),該任務(采用回調函數的形式)才會進入主線程執行。排在異步任務后面的代碼,不用等待異步任務結束會馬上運行,也就是說,異步任務不具有“堵塞”效應。
舉例來說,Ajax 操作可以當作同步任務處理,也可以當作異步任務處理,由開發者決定。如果是同步任務,主線程就等著 Ajax 操作返回結果,再往下執行;如果是異步任務,主線程在發出 Ajax 請求以后,就直接往下執行,等到 Ajax 操作有了結果,主線程再執行對應的回調函數。
三、任務隊列和事件循環
JavaScript 運行時,除了一個正在運行的主線程,引擎還提供一個任務隊列(task queue),里面是各種需要當前程序處理的異步任務。(實際上,根據異步任務的類型,存在多個任務隊列。為了方便理解,這里假設只存在一個隊列。)
首先,主線程會去執行所有的同步任務。等到同步任務全部執行完,就會去看任務隊列里面的異步任務。如果滿足條件,那么異步任務就重新進入主線程開始執行,這時它就變成同步任務了。等到執行完,下一個異步任務再進入主線程開始執行。一旦任務隊列清空,程序就結束執行。
異步任務的寫法通常是回調函數。一旦異步任務重新進入主線程,就會執行對應的回調函數。如果一個異步任務沒有回調函數,就不會進入任務隊列,也就是說,不會重新進入主線程,因為沒有用回調函數指定下一步的操作。
JavaScript 引擎怎么知道異步任務有沒有結果,能不能進入主線程呢?答案就是引擎在不停地檢查,一遍又一遍,只要同步任務執行完了,引擎就會去檢查那些掛起來的異步任務,是不是可以進入主線程了。這種循環檢查的機制,就叫做事件循環(Event Loop)。維基百科的定義是:“事件循環是一個程序結構,用于等待和發送消息和事件(a programming construct that waits for and dispatches events or messages in a program)”。
范例1
let timer1 = setTimeout(() => {console.log(1)}, 0)console.log(2)以上代碼先輸出1,立即輸出2 延時0ms和延時1ms效果其實是一樣的,定時器有最小時間粒度。[1]定時器最小時間間隔:在蘋果機上的最小時間間隔是10ms,在Windows系統上的最小時間間隔大約是15ms。Firefox中定義的最小時間間隔是10ms,而HTML5規范中定義的最小時間間隔是4ms
范例2
let t = ture let timer = setTimeout(() {t = false }, 1000) while (t) {} console.log('end');以上代碼是一個死循環,結果是瀏覽器卡死,永遠不會輸出'end'
范例3
const useTime = t => {let start = Date.now()while (Date.now() - start < t) {} } let timer1 = setTimeout(() => {console.log(3) }, 500) let timer2 = setTimeout(() => {console.log(4) }, 1000) console.log(1) useTime(2000) console.log(2)以上代碼立即輸出1,等待2秒后同時依次輸出2、3、4,因為setTimeout是異步任務,而timer1比time2先注冊,且timer1比timer2定時時間短。又因為useTime函數執行時間是2s,所以執行完之后,將立即執行任務隊列里的timer1、timer2,所以等待2秒后將依次輸出2、3、4。
執行流程
1、進入全局執行上下文
創建由第三方計時模塊管理的timer1,timer1會在500ms后把任務fn1放入任務隊列
創建由第三方計時模塊管理的timer2,time2會在1000ms后把任務fn2放入任務隊列
輸出1
2、進入useTime執行上下文
執行代碼耗時2000ms,退出useTime執行上下文
在500ms和1000ms時timer1和timer2各自完成投放,此操作不屬于JS主線程
3、返回全局執行上下文
輸出2
同步任務執行完畢 開始掃描任務隊列
取出隊列的fn1
4、進入fn1執行上下文
輸出3,退出fn1執行上下文
取出隊列的fn2
5、進入fn2執行上下文
輸出4,退出fn2執行上下文
循環掃描任務隊列
線程、事件循環和任務隊列
Javascript是單線程的,但是卻能執行異步任務,這主要是因為 JS 中存在事件循環(Event Loop)和任務隊列(Task Queue)。事件循環:JS 會創建一個類似于 while (true) 的循環,每執行一次循環體的過程稱之為Tick。每次Tick的過程就是查看是否有待處理事件,如果有則取出相關事件及回調函數放入執行棧中由主線程執行。待處理的事件會存儲在一個任務隊列中,也就是每次Tick會查看任務隊列中是否有需要執行的任務。任務隊列:異步操作會將相關回調添加到任務隊列中。而不同的異步操作添加到任務隊列的時機也不同,如onclick, setTimeout,ajax 處理的方式都不同,這些異步操作是由瀏覽器內核的webcore來執行的,webcore包含下圖中的3種 webAPI,分別是DOM Binding、network、timer模塊。
- DOM Binding 模塊處理一些DOM綁定事件,如onclick事件觸發時,回調函數會立即被webcore添加到任務隊列中。
- network 模塊處理Ajax請求,在網絡請求返回時,才會將對應的回調函數添加到任務隊列中。
- timer 模塊會對setTimeout等計時器進行延時處理,當時間到達的時候,才會將回調函數添加到任務隊列中。
主線程:JS 只有一個線程,稱之為主線程。而事件循環是主線程中執行棧里的代碼執行完畢之后,才開始執行的。所以,主線程中要執行的代碼時間過長,會阻塞事件循環的執行,也就會阻塞異步操作的執行。只有當主線程中執行棧為空的時候(即同步代碼執行完后),才會進行事件循環來觀察要執行的事件回調,當事件循環檢測到任務隊列中有事件就取出相關回調放入執行棧中由主線程執行。
參考:
饑人谷課件:
https://static.xiedaimala.com/xdml/file/f40ceb64-df08-4420-9226-7f76dbff15d5/2019-12-20-11-14-11.pdf?static.xiedaimala.comjavascript中單線程和任務隊列?www.jianshu.com參考
總結
以上是生活随笔為你收集整理的javascript 等待指定时间_javascript的单线程和任务队列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: backlog配置_TCP/IP协议中b
- 下一篇: 如何用python画转盘_如何用ppt做