Execute code, delayed function depends on wait

In the minimum example below, replacing old content is deferred to setTimeout to give the user time to view it. In the meantime, new content is being created to avoid blocking the user interface during a potentially costly task.

 var div = document.getElementById('wrapper'); var newContent = document.createElement('ul'); setTimeout(function() { var header = div.firstElementChild; header.innerHTML = 'New Content'; header.nextElementSibling.remove(); div.appendChild(newContent); }, 2000); // Make new content while we wait [1, 10, 100, 1000].forEach(function(x) { var li = document.createElement('li'); li.innerHTML = 'Factorial of ' + x + ' is ' + factorial(x); newContent.appendChild(li); }); function factorial(num) { if (num === 0) { return 1; } else { return (num * factorial(num - 1)); } } 
 <div id='wrapper'> <h1>Old content</h1> <p>Read it before it gone.</p> </div> 

My concern with this approach is that it does not seem to handle newContent without being ready when a replacement should occur. I'm also not sure if this approach will block the user interface or if the task used by setTimeout will be running at the same time.

How can I guarantee that the user interface will not be blocked during a potentially expensive task and will be used immediately after completion?

+6
source share
2 answers

You have two requirements:

  • Do not hide the introduction until 2 seconds have passed.
  • Do not hide the introduction until the content is ready.

The following are the changes below.

 <html> <body> <div id='wrapper'> <h1>Old content</h1> <p>Read it before it gone.</p> </div> <script> var div = document.getElementById('wrapper'); var newContent = document.createElement('ul'); var contentReady = false; var timesUp = false; function onContentReady() { if (! timesUp || ! contentReady) return; var header = div.firstElementChild; header.innerHTML = 'New Content'; header.nextElementSibling.remove(); div.appendChild(newContent); } setTimeout(function() { timesUp = true; onContentReady(); } , 2000); function makeContent() { // Make new content while we wait [1, 10, 100, 1000].forEach(function(x) { var li = document.createElement('li'); li.innerHTML = 'Factorial of ' + x + ' is ' + factorial(x); newContent.appendChild(li); }); contentReady = true; onContentReady(); } function factorial(num) { if (num === 0) { return 1; } else { return (num * factorial(num - 1)); } } setTimeout(function() { makeContent(); } , 4000); </script> </body> </html> 

Change the time in this code for less than 2 seconds and more than two seconds to see this.

 setTimeout(function() { makeContent(); } , 4000); 
+3
source

Your long-term calculation blocks the browser, which will never be a good idea. Therefore, you should put it in a web artist.

These days it is better to write asynchronous code with asynchronous tools like promises. The general pseudo-code level approach is used here:

 // Create a promise which fulfills after some # of ms. function timeout(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } // Create the worker, kick it off, and // return a promise which fulfills when the worker reports a result. function waitWorker() { const worker = new Worker('factorial.js'); worker.postMessage([1, 10, 100, 1000]); return new Promise(resolve => worker.addEventListener('message', event => resolve(event.data)) ); } // Wait for both the worker to complete and the two seconds to elapse. // Then output the data. Promise.all([timeout(2000), waitWorker()]) .then(values => output(values[1]); 

Writing a worker remains in the form of an exercise.

Using Asynchronous Functions

You can also express this more purely using asynchronous functions, if your environment supports it, as follows:

 async function calcAndWait() { const result = waitWorker(); // Kick off computation. await timeout(ms); // Wait for two seconds. output(await result); // Wait for computation to finish and output. } 
+8
source

All Articles