Home > other >  Temporarily unfreeze UI to allow a DOM update
Temporarily unfreeze UI to allow a DOM update

Time:08-24

I have some code that functions like so. It will iterate over an array with forEach, perform a heavy task that freezes the UI with the current iteration, push the returned value from that heavy task to an array, and then return the array being pushed to after the forEach has concluded. This is how it would work:

// generate a dummy array
var array = Array.from(Array(1000000).keys()):

function doSomething() {
    // the array to push to
    var pushArray = [];

    array.forEach((item) => {
        // perform a heavy task...
        const output = doHeavyThing(item);

        // ...and push the heavy task result to an array
        pushArray.push(output);

        // (this DOM call will not work, because doHeavyThing blocks the UI)
        document.getElementById('updateDiv').innerHTML = item;
    })

    // return the array that was pushed to
    return pushArray;
}

const result = doSomething();
return result;

Is there any way I can temporarily pause the execution of the forEach loop, unfreezing the UI, to allow for a DOM update? This would only require a couple of milliseconds.

I cannot use web workers in this situation, because the data being returned is so large that any calls to postMessage would crash the browser on mobile.

I also haven't been able to get setTimeout to work, because it is asynchronous - meaning I cannot return anything. I also cannot reliably use callbacks.

Does anyone have an idea on how to solve this problem? Clarifying questions are always welcome.

My doHeavyThing function is a function from the seek-bzip package:

bzip.decodeBlock(compressedDataBlock, 32);

CodePudding user response:

I believe something like this would work to ensure the event loop can tick in between calls to doHeavyThing.

// create a promise that will resolve on the next tick of the event loop
function sleep() { 
    return new Promise(r => setTimeout(r));
}

// standin for doHeavyThing. This is enough to induce
// noticable delay on my machine, tweak the # of iterations
// if it's too fast to notice on yours
function doHeavyThing() {
  for (let i = 0; i < 100; i  ) { console.log(i); }
}

async function handleHeavyLifting() {
    const array = Array.from(Array(1000).keys());
    const result = [];
    for (const item of array) {
      document.getElementById('updateDiv').innerHTML = item;
      result.push(doHeavyThing(item));
      // let DOM updates propagate, other JS callbacks run, etc
      await sleep();
    }
    return result;
}

handleHeavyLifting();
<div id="updateDiv">
None
</div>


  • Related