首頁 web前端 js教程 寫一個JavaScript框架:比setTimeout更好的定時執行

寫一個JavaScript框架:比setTimeout更好的定時執行

Nov 29, 2016 pm 04:24 PM
javascript

這是 JavaScript 框架系列的第二章。在這一章裡,我打算講一下在瀏覽器裡的非同步程式碼不同執行方式。你將了解定時器和事件循環之間的不同差異,例如 setTimeout 和 Promises。

這個系列是關於一個開源的客戶端框架,叫做 NX。在這個系列裡,我主要解釋一下寫該框架不得不克服的主要困難。如果你對 NX 有興趣可以參觀我們的 主頁。

這個系列包含以下幾個章節:

專案結構

定時執行(當前章節)

沙箱代碼評估

資料綁定介紹

資料綁定與ES6 資料代理

資料綁定介紹

端路由

非同步程式碼執行

你可能比較熟悉Promise、process.nextTick()、setTimeout(),或許還有requestAnimationFrame() 這些非同步執行程式碼的方式。它們內部都使用了事件循環,但是它們在精確計時方面有一些不同。

在這一章裡,我將解釋它們之間的不同,然後給大家演示怎樣在一個類似 NX 這樣的先進框架裡面實現一個定時系統。不用我們重新做一個,我們將使用原生的事件循環來達到我們的目的。

事件循環

事件循環甚至沒有在 ES6 規範中提到。 JavaScript 本身只有任務(Job)和任務佇列(job queue)。更複雜的事件循環是在 NodeJS 和 HTML5 規範裡分別定義的,因為這篇是針對前端的,我會在詳細說明後者。

事件循環可以被看做某個條件的循環。它不停的尋找新的任務來運作。這個循環中的一次迭代叫做一個滴答(tick)。在一次滴答期間執行的程式碼稱為一次任務(task)。

while (eventLoop.waitForTask()) {   
  eventLoop.processNextTask() 
}
登入後複製

任務是同步程式碼,它可以在循環中調度其它任務。一個簡單的呼叫新任務的方式是 setTimeout(taskFn)。不管怎樣, 任務可能有很多來源,例如使用者事件、網路或 DOM 操作

寫一個JavaScript框架:比setTimeout更好的定時執行

任務佇列

更複雜一些的是,事件循環可以有多個任務佇列。這裡有兩個約束條件,相同任務來源的事件必須在相同的佇列,以及任務必須依照插入的順序處理。除此之外,瀏覽器可以做任何它想做的事情。例如,它可以決定接下來要處理哪個任務佇列。

while (eventLoop.waitForTask()) {   
  const taskQueue = eventLoop.selectTaskQueue() 
  if (taskQueue.hasNextTask()) { 
    taskQueue.processNextTask() 
  } 
}
登入後複製

用這個模型,我們不能精確的控制定時。如果用 setTimeout()瀏覽器可能決定先執行完其它幾個佇列才執行我們的佇列。

寫一個JavaScript框架:比setTimeout更好的定時執行

微任務隊列

幸運的是,事件循環還提供了一個叫做微任務(microtask)隊列的單一隊列。當目前任務結束的時候,微任務佇列會清空每個滴答裡的任務。

while (eventLoop.waitForTask()) {   
  const taskQueue = eventLoop.selectTaskQueue() 
  if (taskQueue.hasNextTask()) { 
    taskQueue.processNextTask() 
  } 
  const microtaskQueue = eventLoop.microTaskQueue 
  while (microtaskQueue.hasNextMicrotask()) { 
    microtaskQueue.processNextMicrotask() 
  } 
}
登入後複製

最簡單的呼叫微任務的方法是 Promise.resolve().then(microtaskFn)。微任務按照插入順序進行處理,並且由於僅存在一個微任務隊列,瀏覽器不會把時間弄亂了。

此外,微任務可以調度新的微任務,它將插入到同一個隊列,並在同一個滴答內處理。

寫一個JavaScript框架:比setTimeout更好的定時執行

繪製Rendering

最後是繪製Rendering調度,不同於事件處理和分解,繪製並不是在單獨的後台任務完成的。它是一個可以運行在每個循環滴答結束時的演算法。

在這裡瀏覽器又有了許多自由:它可能在每個任務以後繪製,但是它也可能在好幾百個任務都執行了以後也不繪製。

幸運的是,我們有 requestAnimationFrame(),它在下一個繪製之前執行傳遞的函數。我們最終的事件模型像這樣:

while (eventLoop.waitForTask()) {   
  const taskQueue = eventLoop.selectTaskQueue() 
  if (taskQueue.hasNextTask()) { 
    taskQueue.processNextTask() 
  } 
  const microtaskQueue = eventLoop.microTaskQueue 
  while (microtaskQueue.hasNextMicrotask()) { 
    microtaskQueue.processNextMicrotask() 
  } 
  if (shouldRender()) { 
    applyScrollResizeAndCSS() 
    runAnimationFrames() 
    render() 
  } 
}
登入後複製

現在用我們所知道知識來創建定時系統!

利用事件循環

和大多數現代框架一樣,NX 也是基於 DOM 操作和資料綁定的。批次操作和非同步執行以取得更好的效能表現。基於以上理由我們用 Promises、 MutationObservers 和 requestAnimationFrame()。

我們所期望的定時器是這樣的:

程式碼來自於開發者

資料綁定和DOM 操作由NX 來執行

開發者定義事件鉤子

瀏覽器進行繪製

開發者定義事件鉤子

瀏覽器進行繪製

開發者定義事件鉤子

NX 暫存器物件基於ES6 代理程式以及DOM 變動是基於MutationObserver (變動觀測器)同步運作(下一節詳細介紹)。 它作為一個微任務延遲直到步驟 2 執行以後才做出反應。這個延遲已經在Promise.resolve().then(reaction) 進行了物件轉換,並且它將透過變動觀測器自動運行。

步驟 2

🎜來自開發者的程式碼(任務)運作完成。微任務由 NX 開始執行所註冊。 因為它們是微任務,所以按序執行。請注意,我們仍然在同一個滴答循環中。 🎜

步骤 3

开发者通过 requestAnimationFrame(hook) 通知 NX 运行钩子。这可能在滴答循环后发生。重要的是,钩子运行在下一次绘制之前和所有数据操作之后,并且 DOM 和 CSS 改变都已经完成。

步骤 4

浏览器绘制下一个视图。这也有可能发生在滴答循环之后,但是绝对不会发生在一个滴答的步骤 3 之前。

牢记在心里的事情

我们在原生的事件循环之上实现了一个简单而有效的定时系统。理论上讲它运行的很好,但是还是很脆弱,一个轻微的错误可能会导致很严重的 BUG。

在一个复杂的系统当中,最重要的就是建立一定的规则并在以后保持它们。在 NX 中有以下规则:

永远不用 setTimeout(fn, 0) 来进行内部操作

用相同的方法来注册微任务

微任务仅供内部操作

不要干预开发者钩子运行时间

规则 1 和 2

数据反射和 DOM 操作将按照操作顺序执行。这样只要不混合就可以很好的延迟它们的执行。混合执行会出现莫名其妙的问题。

setTimeout(fn, 0) 的行为完全不可预测。使用不同的方法注册微任务也会发生混乱。例如,下面的例子中 microtask2 不会正确地在 microtask1 之前运行。

Promise.resolve().then().then(microtask1)   
Promise.resolve().then(microtask2)
登入後複製

寫一個JavaScript框架:比setTimeout更好的定時執行

分离开发者的代码执行和内部操作的时间窗口是非常重要的。混合这两种行为会导致不可预测的事情发生,并且它会需要开发者了解框架内部。我想很多前台开发者已经有过类似经历。

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

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統 如何利用JavaScript和WebSocket實現即時線上點餐系統 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統 如何使用WebSocket和JavaScript實現線上預約系統 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

javascript如何使用insertBefore javascript如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

如何在JavaScript中取得HTTP狀態碼的簡單方法 如何在JavaScript中取得HTTP狀態碼的簡單方法 Jan 05, 2024 pm 01:37 PM

JavaScript中的HTTP狀態碼取得方法簡介:在進行前端開發中,我們常常需要處理與後端介面的交互,而HTTP狀態碼就是其中非常重要的一部分。了解並取得HTTP狀態碼有助於我們更好地處理介面傳回的資料。本文將介紹使用JavaScript取得HTTP狀態碼的方法,並提供具體程式碼範例。一、什麼是HTTP狀態碼HTTP狀態碼是指當瀏覽器向伺服器發起請求時,服務

See all articles