An overview of
We all know that JS is single threaded, and methods such as setTimeout and setInterval do not execute exactly as we expect. You can usually use the functions setTimeout or setInterval to execute a periodic event, and for simple tasks, the goal can be achieved. The real problem is that when you have multiple tasks, interspersing them with time-consuming actions can affect the time of the next task. So there are a few things we can do to make it as timely as possible.
Method 1 uses the Date class
We can use the system clock to compensate for the inaccuracy of the timer. We calculate the error from a series of setTimeout calls and subtract it from the next iteration.
const PENDING = 'PENDING'
const STARTED = 'STARTED'
const FULFILLED = 'FULFILLED'
class accurTimer {
constructor(options) {
this.state = PENDING
this.count = 0
this.timer = options.timer || 100
this.max = options.max || 1
this.repeatArgument = options.repeatArgument || (() = > {})
this.onFulfilled = null
}
start() {
this.state = STARTED
let timeStart = new Date().getTime();
this.timerId = setTimeout(this.loop.bind(this,timeStart),this.timer)
return this
}
loop(timeStart) {
if(this.count < this.max) {
let executime = new Date().getTime()
let fix = executime - timeStart - this.timer
setTimeout(this.loop.bind(this,executime),this.timer - fix)
this.repeatArgument(this.count)
this.count++
} else {
this.state = FULFILLED
if(this.onFulfilled) {
return this.onFulfilled()
}
}
}
finish(onFulfilled) {
if(this.state == FULFILLED) {
return onFulfilled()
} else {
this.onFulfilled = onFulfilled
}
}
stop() {
clearTimeout(this.timerId)
}
}
Copy the code
We can use it to simulate type just like the tailwindcss home page does to simulate code input. As a demo
Method 2: Use the Web worker
We can use Web workers to place periodic tasks in the worker to minimize the influence of the main thread.
const worker = ` const timerIdToId = {}; onmessage = function (event) { let data = event.data, name = data.name, timerId = data.TimerId, time; if(data.hasOwnProperty('time')) { time = data.time; } switch (name) { case 'setInterval': timerIdToId[timerId] = setInterval(function () { postMessage({timerId: timerId}); }, time); break; case 'clearInterval': if (timerIdToId.hasOwnProperty (timerId)) { clearInterval(timerIdToId[timerId]); delete timerIdToId[timerId]; } break; case 'setTimeout': timerIdToId[timerId] = setTimeout(function () { postMessage({timerId: timerId}); if (timerIdToId.hasOwnProperty (timerId)) { delete timerIdToId[timerId]; } }, time); break; case 'clearTimeout': if (timerIdToId.hasOwnProperty (timerId)) { clearTimeout(timerIdToId[timerId]); delete timerIdToId[timerId]; } break; }} `
class WebWorker {
static maxTimerId = 0x7FFFFFFF;
constructor(worker) {
const blob = new Blob([worker])
this.callbackList = {}
this.lastTimerId = 0
this.generateTimerId = this.generateTimerId.bind(this)
this.setTimeout = this.setTimeout.bind(this)
this.setInterval = this.setInterval.bind(this)
this.clearTimeout = this.clearTimeout.bind(this)
this.clearInterval = this.clearInterval.bind(this)
this.webWorker = new Worker(URL.createObjectURL(blob))
return Object.assign(this.webWorker,this)}generateTimerId() {
do {
if (this.lasttimerId == this.maxTimerId) {
this.lasttimerId = 0;
} else {
this.lasttimerId ++; }}while (this.callbackList.hasOwnProperty(this.lasttimerId));
return this.lasttimerId;
}
setTimeout(callback,timer) {
const timerId = this.generateTimerId()
this.callbackList[timerId] = {
callback: callback,
isTimeout: true
};
this.webWorker.postMessage({
name: 'setTimeout'.TimerId: timerId,
time: timer
})
return timerId
}
clearTimeout(timerId) {
if (this.callbackList.hasOwnProperty(timerId)) {
delete this.callbackList[timerId];
this.webWorker.postMessage ({
name: 'clearTimeout'.timerId: timerId }); }}setInterval(callback,timer) {
const timerId = this.generateTimerId()
this.callbackList[timerId] = {
callback: callback
};
this.webWorker.postMessage({
name: 'setInterval'.TimerId: timerId,
time: timer
})
return timerId
}
clearInterval (timerId) {
if (this.callbackList.hasOwnProperty(timerId)) {
delete this.callbackList[timerId];
this.webWorker.postMessage ({
name: 'clearInterval'.timerId: timerId }); }}}const webWorker = new WebWorker(worker)
webWorker.onmessage = function (event) {
let data = event.data,
timerId = data.timerId
if (this.callbackList.hasOwnProperty(timerId)) {
request = this.callbackList[timerId]
callback = request.callback
if (request.hasOwnProperty ('isTimeout') && request.isTimeout) {
delete this.callbackList[timerId];
}
callback()
}
};
let timerId = webWorker.setInterval(() = > {console.log('setInterval')},1000)
webWorker.setTimeout(() = > {webWorker.clearInterval(timerId)},3000)
Copy the code