[Javascript + Performance] How to run a large number of time-consuming tasks and doesn't block rendering
Try option 1: Promise
Promise running in Microtask queue, and rendering should wait until the queue is empty; If you have a large number of time-consuming in microtask, it will also block rendering
function runTask(task) {
Promise.resolve().then(() => {
task()
})
}
So this is NOT a soltuion.
Try option 2: SetTimeout
function runTask(task) {
setTimeout(() => {
task()
}, 0)
}
setTimeout run in Marctasks queue, it will block block the rendering but it will cause rendering to be laggy. Why?
for (;;) {
// pick first task from marcotask queue
// do the task
// check whether it's good time to do the rendering? if yes then render
}
The reason it's not cause blocking rendering, due to everytime it only pick one task.
Laggy is due to different browser might have different handling logic, for example, Chrome might lag but Safari might not.
So setTimeout also doesn't seem to be a good option.
Try option 3: requestAnimationFrame
function runTask(task) {
requestAnimationFrame(() => {
task()
})
}
It will block rendering for sure, becaue before each rendering, browser need to perform task()
which will block rendering.
Try option 4: write custom code to (requestIdleCallback)
function _runTask(task) {
requestIdleCallback(idle => {
if (idle.timeRemaining()>0) {
task()
} else {
_runTask(task)
}
})
}
function runTask(task) {
_runTask(task)
}
The
window.requestIdleCallback()
method queues a function to be called during a browser's idle periods. This enables developers to perform background and low priority work on the main event loop, without impacting latency-critical events such as animation and input response. Functions are generally called in first-in-first-out order; however, callbacks which have atimeout
specified may be called out-of-order if necessary in order to run them before the timeout elapses.
https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
It seems that requestidleCallback
can perform the task well, there is no blocking render.
But there is a problem about browser support, https://caniuse.com/requestidlecallback, as you can see, not all the browsers support this API. If we want to support more browsers, we still need to improve it.
Try option 5: custom code with requestAnimationFrame
function _runTask(task) {
const startTime = Date.now()
requestAnimationFrame(() => {
if (Date.now() - startTime < 16.6) {
task()
} else {
_runTask(task)
}
})
}
function runTask(task) {
_runTask(task)
}
We just need to calculate is there enough time to run the task, then we do it, otherwise wait for next tick.