Updating the DOM before blocking code with setTimeout or promise

I know that if you have a processor with intensive code, any immediate previous DOM update will not happen. For instance,

function blockFor(dur){ var now = new Date().getTime(); while (new Date().getTime() < now + dur); result.textContent = "I am done..!"; } result.textContent = "Please remain..."; // we will never see this blockFor(2000); 
 <p id="result"></p> 

However, if I switch the processor intensive code to the asynchronous timeline on setTimeout , everything will be fine, as in the following fragment.

 function blockFor(dur){ var now = new Date().getTime(); while (new Date().getTime() < now + dur); result.textContent = "I am done..!"; } result.textContent = "Please remain..."; // now you see me setTimeout(_ => blockFor(2000),15); // 15ms to be on the safe side 
 <p id="result"></p> 

However, since I know that promises will also lead you to the β€œsort” of an asynchronous timeline, I expected to achieve the same effect without using the setTimeout hack. For instance:

 function blockFor(dur){ var now = new Date().getTime(); while (new Date().getTime() < now + dur); result.textContent = "I am done..!"; } result.textContent = "Please remain..."; // not in Chrome not in FF Promise.resolve(2000) .then(blockFor) 
 <p id="result"></p> 

I would at least expect this to happen in FF because of this perfect article ( https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/ ), alas, nothing.

Is there a way to accomplish this task with promises?

+4
source share
3 answers

Promise.prototype .then has microtask semantics. This means that it must wait for the synchronous code to execute, but not to run the asynchronous code - browsers will probably prefer that all JS start before the DOM updates are executed.

As a rule, microtask means that it needs to wait for another JS to start, and then it can work before it brings control to a non-JS code.

setTimeout has setTimeout semantics. It works as part of the DOM API, and when the callback works, code other than js is already able to run. Browsers already run their own code when it runs, so they also handle events and DOM updates.

Usually a macro means that he needs to wait for the launch of all the other JSs, as well as for the β€œtick of the loop”, that is: events to run.

This is also the difference between setImmediate and nextTick in NodeJS .

To answer your question directly: no. . It is not possible to force the browser to run DOM updates in the microtype update - while it is not forbidden for it, it will be a "bad manners".

For lengthy processor-bound operations - can I offer Web Workers instead?

+3
source

The problem is that the promise, even if it is executed asynchronously, is fulfilled too early. Thus, browsers do not have time to update the DOM. This problem does not apply to promises, I see the same result when using setTimeout with a 0ms delay:

 function blockFor(dur){ var now = new Date().getTime(); while (new Date().getTime() < now + dur); result.textContent = "I am done..!"; } result.textContent = "Please remain..."; // we will never see this setTimeout(_ => blockFor(2000), 0); // 0ms is not enough 
 <p id="result"></p> 

Actually, it seems to you that you want requestAnimationFrame :

 function blockFor(dur){ var now = new Date().getTime(); while (new Date().getTime() < now + dur); result.textContent = "I am done..!"; } result.textContent = "Please remain..."; // now you see me new Promise(function(resolve) { requestAnimationFrame(_ => resolve(2000)); }).then(blockFor); 
 <p id="result"></p> 

But at this point you can only use requestAnimationFrame , without promises.

 function blockFor(dur){ var now = new Date().getTime(); while (new Date().getTime() < now + dur); result.textContent = "I am done..!"; } result.textContent = "Please remain..."; // now you see me requestAnimationFrame(_ => blockFor(2000)); 
 <p id="result"></p> 
+1
source

The best way to do this is to delegate the heavy process to the web worker ...

 // main thread document.getElementById("result").addEventListener('click', handleClick); const worker = new Worker('worker.js'); function handleClick(){ worker.onmessage = e => { console.log('main', e.data.response) this.textContent = e.data.response; } this.textContent = "Please remain..."; worker.postMessage({data: 2000}); } // worker self.addEventListener('message', e => { const { data } = e.data; console.log('worker', data); function blockFor(dur){ var now = new Date().getTime(); while (new Date().getTime() < now + dur); } blockFor(data) self.postMessage({ response: "I am done..!" }); }); // NOTE: perform this test on your app for browser compatibility if (window.Worker) { ... } 

Check out this live code.

MDN web workers documents

0
source

Source: https://habr.com/ru/post/1270621/


All Articles