摘要:更方便的在于,由于自帶定時(shí)器功能,我們甚至不用自己去維護(hù)一個(gè)時(shí)間戳。請(qǐng)注意這里由于沒有調(diào)用另一個(gè)腳本,我們通過和的方式將我們的定時(shí)器程序傳入中。
問題
經(jīng)常使用Javascript的同學(xué)一定對(duì)setInterval非常熟悉,當(dāng)使用setInterval(callback, timer)時(shí),每經(jīng)過timer毫秒時(shí)間,系統(tǒng)都將調(diào)用一次callback。請(qǐng)問全局如果沒有提供setInterval函數(shù),該如何自己實(shí)現(xiàn)這一功能?
方案一:循環(huán)或遞歸(錯(cuò)誤解法)最簡(jiǎn)單的思路便是通過簡(jiǎn)單的循環(huán)或者遞歸,每次檢查時(shí)間戳是否已經(jīng)超過上次觸發(fā)給定函數(shù)的時(shí)間加上間隔時(shí)間,如果已經(jīng)超過便再次觸發(fā)函數(shù),并重置計(jì)時(shí)器至當(dāng)前時(shí)間。
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; }
然而這樣的解法有一個(gè)致命問題,我們將setInterval1變成一個(gè)阻塞函數(shù),主線程會(huì)卡死在這個(gè)無限循環(huán)或者遞歸中,導(dǎo)致之后的代碼或者事件無法執(zhí)行。想了解詳細(xì)原因的請(qǐng)戳: 并發(fā)模型與事件循環(huán),JavaScript:徹底理解同步、異步和事件循環(huán)(Event Loop)
方案二:使用setTimeoutsetTimeout的好處在于,它是在消息隊(duì)列里面添加一個(gè)待執(zhí)行的消息,所以并不會(huì)堵塞主線程。更方便的在于,由于setTimeout自帶定時(shí)器功能,我們甚至不用自己去維護(hù)一個(gè)時(shí)間戳。我們可以通過不斷遞歸調(diào)用setTimeout來實(shí)現(xiàn)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有違這道題的初衷,因?yàn)?b>setTimeout在本質(zhì)上和setInterval是類似的,多少有些作弊的嫌疑。那有沒有別的非阻塞方案呢?在瀏覽器環(huán)境中,我們有requestAnimationFrame(),而在nodejs環(huán)境中,我們有setImmediate()。以requestAnimationFrame為例,這將保證我們的代碼只會(huì)在每一幀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能確保我們?cè)诿繋@示前被調(diào)用一次,從而檢計(jì)時(shí)器是否到期,但是如果被執(zhí)行的函數(shù)計(jì)算量極大,導(dǎo)致幀內(nèi)無法完成時(shí),該如何保證給定函數(shù)能按時(shí)執(zhí)行呢?顯然,此時(shí)只依靠主線程來確保計(jì)時(shí)程序和給定程序都能準(zhǔn)確執(zhí)行,有點(diǎn)困難,但是如果將計(jì)時(shí)程序放入另一線程中,而主程序只負(fù)責(zé)監(jiān)聽定時(shí)器事件和執(zhí)行給定程序,是不是會(huì)好一些呢?所以我們這里利用瀏覽器提供的Web Worker API來實(shí)現(xiàn)多線程。請(qǐng)注意這里由于沒有調(diào)用另一個(gè)腳本,我們通過blob和object url的方式將我們的定時(shí)器程序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() }
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93884.html
摘要:由于引擎同一時(shí)間只執(zhí)行一段代碼這是由單線程的性質(zhì)決定的,所以每個(gè)代碼塊阻塞了其它異步事件的進(jìn)行。這意味著瀏覽器將等待著一個(gè)新的異步事件發(fā)生。異步的任務(wù)執(zhí)行的順序是不固定的,主要看返回的速度。 我們經(jīng)常說JS是單線程的,比如node.js研討會(huì)上大家都說JS的特色之一是單線程的,這樣使JS更簡(jiǎn)單明了,可是大家真的理解所謂JS的單線程機(jī)制嗎?單線程時(shí),基于事件的異步機(jī)制又該當(dāng)如何,這些知識(shí)...
摘要:定義對(duì)象,用于保存映射到真實(shí)每調(diào)用一次就會(huì)自增的一個(gè)這里注意要使用局部變量保存哦,避免函數(shù)內(nèi)部直接引用,因?yàn)榭赡軙?huì)再次變化說明使用時(shí)除了需要加上一個(gè)對(duì)象做命名空間外其實(shí)也是沒辦法哦,因?yàn)楹托枰蚕硪粋€(gè)叫做的映射表,其他與直接調(diào)用原生,無異舉 talk is cheap: var util = (function(){ //定義intervalObj對(duì)象,用于保存interval...
摘要:?jiǎn)尉€程的話,如果我們做一些的操作比如說這是一個(gè)耗時(shí)的操所那么在這將近一秒內(nèi),線程就會(huì)被阻塞,無法繼續(xù)執(zhí)行下面的任務(wù)。事件循環(huán)的主要機(jī)制就是任務(wù)隊(duì)列機(jī)制一個(gè)事件循環(huán)有一個(gè)或者多個(gè)任務(wù)隊(duì)列。 瀏覽器中的事件循環(huán)機(jī)制 網(wǎng)上一搜事件循環(huán), 很多文章標(biāo)題的前面會(huì)加上 JavaScript, 但是我覺得事件循環(huán)機(jī)制跟 JavaScript 沒什么關(guān)系, JavaScript 只是一門解釋型語言, ...
摘要:主線程會(huì)暫時(shí)存儲(chǔ)等異步操作,直接向下執(zhí)行,當(dāng)某個(gè)異步事件觸發(fā)時(shí),再通知主線程執(zhí)行相應(yīng)的回調(diào)函數(shù),通過這種機(jī)制,避免了單線程中異步操作耗時(shí)對(duì)后續(xù)任務(wù)的影響。 背景 在研究js的異步的實(shí)現(xiàn)方式的時(shí)候,發(fā)現(xiàn)了JavaScript 中的 macrotask 和 microtask 的概念。在查閱了一番資料之后,對(duì)其中的執(zhí)行機(jī)制有所了解,下面整理出來,希望可以幫助更多人。 先了解一下js的任務(wù)執(zhí)...
摘要:一什么是定時(shí)器提供了一些原生方法來實(shí)現(xiàn)延時(shí)去執(zhí)行某一段代碼,下面來簡(jiǎn)單介紹一下設(shè)置一個(gè)定時(shí)器,在定時(shí)器到期后執(zhí)行一次函數(shù)或代碼段定時(shí)器延遲后執(zhí)行的函數(shù)延遲后執(zhí)行的代碼字符串,不推薦使用原理類似延遲的時(shí)間單位毫秒,默認(rèn)值為向延遲函數(shù)傳遞而外的 一、什么是定時(shí)器 JS提供了一些原生方法來實(shí)現(xiàn)延時(shí)去執(zhí)行某一段代碼,下面來簡(jiǎn)單介紹一下 setTimeout: 設(shè)置一個(gè)定時(shí)器,在定時(shí)器到期后執(zhí)行...
閱讀 2083·2021-09-22 15:54
閱讀 1844·2021-09-04 16:40
閱讀 869·2019-08-30 15:56
閱讀 2632·2019-08-30 15:44
閱讀 2159·2019-08-30 13:52
閱讀 1132·2019-08-29 16:35
閱讀 3352·2019-08-29 16:31
閱讀 2571·2019-08-29 13:48