日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

写一个 JavaScript 框架:比 setTimeout 更棒的定时执行

發布時間:2024/4/13 javascript 62 豆豆
生活随笔 收集整理的這篇文章主要介紹了 写一个 JavaScript 框架:比 setTimeout 更棒的定时执行 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這個系列是關于一個開源的客戶端框架,叫做 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 操作。

    任務隊列

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

  • while (eventLoop.waitForTask()) {
  • const taskQueue = eventLoop.selectTaskQueue()
  • if (taskQueue.hasNextTask()) {
  • taskQueue.processNextTask()
  • }
  • }
  • 用這個模型,我們不能精確的控制定時。如果用?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)。微任務按照插入順序進行處理,并且由于僅存在一個微任務隊列,瀏覽器不會把時間弄亂了。

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

    繪制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 來執行
  • 開發者定義事件鉤子
  • 瀏覽器進行繪制
  • 步驟 1

    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)
  • 規則 3 和 4

    分離開發者的代碼執行和內部操作的時間窗口是非常重要的。混合這兩種行為會導致不可預測的事情發生,并且它會需要開發者了解框架內部。我想很多前臺開發者已經有過類似經歷。

    原文發布時間為:2017-11-26

    本文來自云棲社區合作伙伴“Linux中國”

    總結

    以上是生活随笔為你收集整理的写一个 JavaScript 框架:比 setTimeout 更棒的定时执行的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。