[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 a timeout 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 requestidleCallbackcan 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. 

posted @ 2024-08-24 16:40  Zhentiw  阅读(4)  评论(0编辑  收藏  举报