摘要:更方便的在于,由于自帶定時器功能,我們甚至不用自己去維護一個時間戳。請注意這里由于沒有調用另一個腳本,我們通過和的方式將我們的定時器程序傳入中。
問題
經常使用Javascript的同學一定對setInterval非常熟悉,當使用setInterval(callback, timer)時,每經過timer毫秒時間,系統都將調用一次callback。請問全局如果沒有提供setInterval函數,該如何自己實現這一功能?
方案一:循環或遞歸(錯誤解法)最簡單的思路便是通過簡單的循環或者遞歸,每次檢查時間戳是否已經超過上次觸發給定函數的時間加上間隔時間,如果已經超過便再次觸發函數,并重置計時器至當前時間。
const setInterval1 = (func, interval) => { let startTime = Date.now(); const config = { shouldStop: false }; while (!config.shouldStop) { if (Date.now() - startTime >= interval) { func(); startTime = Date.now(); } } return config; } const myClearInterval = config => { config.shouldStop = true; }
然而這樣的解法有一個致命問題,我們將setInterval1變成一個阻塞函數,主線程會卡死在這個無限循環或者遞歸中,導致之后的代碼或者事件無法執行。想了解詳細原因的請戳: 并發模型與事件循環,JavaScript:徹底理解同步、異步和事件循環(Event Loop)
方案二:使用setTimeoutsetTimeout的好處在于,它是在消息隊列里面添加一個待執行的消息,所以并不會堵塞主線程。更方便的在于,由于setTimeout自帶定時器功能,我們甚至不用自己去維護一個時間戳。我們可以通過不斷遞歸調用setTimeout來實現setInterval的效果
const setInterval2 = (func, interval) => { const config = { shouldStop: false } const loop = () => { if (!config.shouldStop) { func(); setTimeout(loop, interval); } } setTimeout(loop, interval); return config; } const myClearInterval = config => { config.shouldStop = true; }方案三:使用requestAnimationFrame
然而使用setTimeout有違這道題的初衷,因為setTimeout在本質上和setInterval是類似的,多少有些作弊的嫌疑。那有沒有別的非阻塞方案呢?在瀏覽器環境中,我們有requestAnimationFrame(),而在nodejs環境中,我們有setImmediate()。以requestAnimationFrame為例,這將保證我們的代碼只會在每一幀render之前被遞歸一次,從而避免了阻塞其他代碼。
const setInterval3 = (func, interval) => { let startTime = Date.now(); const config = { shouldStop: false } const check = () => { if (!config.shouldStop) { if (Date.now() - startTime > interval) { func(); startTime = Date.now(); } if(typeof window === "undefined") { setImmediate(check); } else { window.requestAnimationFrame(check) } } } check(); return config; } const myClearInterval = config => { config.shouldStop = true; }方案四:使用Web Worker
requestAnimationFrame能確保我們在每幀顯示前被調用一次,從而檢計時器是否到期,但是如果被執行的函數計算量極大,導致幀內無法完成時,該如何保證給定函數能按時執行呢?顯然,此時只依靠主線程來確保計時程序和給定程序都能準確執行,有點困難,但是如果將計時程序放入另一線程中,而主程序只負責監聽定時器事件和執行給定程序,是不是會好一些呢?所以我們這里利用瀏覽器提供的Web Worker API來實現多線程。請注意這里由于沒有調用另一個腳本,我們通過blob和object url的方式將我們的定時器程序check傳入Web Worker中。
const setInterval4 = (func, interval) => { if (typeof window !== "undefined" && window.Worker && window.Blob) { const check = new Blob(["(", function(){ self.onmessage = function(e) { const interval = e.data; let startTime = Date.now(); while(true) { if (Date.now() - startTime >= interval) { startTime = Date.now(); self.postMessage(Date.now()); } } } }.toString(), ")()"], { type: "text/javascript" }); const worker = new Worker(window.URL.createObjectURL(check)); worker.onmessage = func; worker.postMessage(interval); return worker; } else { console.log("Your environment is not supported"); } } const myClearInterval = config => { config.terminate() }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93884.html
摘要:定義對象,用于保存映射到真實每調用一次就會自增的一個這里注意要使用局部變量保存哦,避免函數內部直接引用,因為可能會再次變化說明使用時除了需要加上一個對象做命名空間外其實也是沒辦法哦,因為和需要共享一個叫做的映射表,其他與直接調用原生,無異舉 talk is cheap: var util = (function(){ //定義intervalObj對象,用于保存interval...
摘要:單線程的話,如果我們做一些的操作比如說這是一個耗時的操所那么在這將近一秒內,線程就會被阻塞,無法繼續執行下面的任務。事件循環的主要機制就是任務隊列機制一個事件循環有一個或者多個任務隊列。 瀏覽器中的事件循環機制 網上一搜事件循環, 很多文章標題的前面會加上 JavaScript, 但是我覺得事件循環機制跟 JavaScript 沒什么關系, JavaScript 只是一門解釋型語言, ...
摘要:主線程會暫時存儲等異步操作,直接向下執行,當某個異步事件觸發時,再通知主線程執行相應的回調函數,通過這種機制,避免了單線程中異步操作耗時對后續任務的影響。 背景 在研究js的異步的實現方式的時候,發現了JavaScript 中的 macrotask 和 microtask 的概念。在查閱了一番資料之后,對其中的執行機制有所了解,下面整理出來,希望可以幫助更多人。 先了解一下js的任務執...
摘要:一什么是定時器提供了一些原生方法來實現延時去執行某一段代碼,下面來簡單介紹一下設置一個定時器,在定時器到期后執行一次函數或代碼段定時器延遲后執行的函數延遲后執行的代碼字符串,不推薦使用原理類似延遲的時間單位毫秒,默認值為向延遲函數傳遞而外的 一、什么是定時器 JS提供了一些原生方法來實現延時去執行某一段代碼,下面來簡單介紹一下 setTimeout: 設置一個定時器,在定時器到期后執行...
閱讀 2073·2021-09-22 15:54
閱讀 1834·2021-09-04 16:40
閱讀 861·2019-08-30 15:56
閱讀 2628·2019-08-30 15:44
閱讀 2153·2019-08-30 13:52
閱讀 1125·2019-08-29 16:35
閱讀 3347·2019-08-29 16:31
閱讀 2567·2019-08-29 13:48