2

I was testing the Promise object and wrote some code that simulates a long running task that is synchronous. I was comparing Promise and setTimeout - see fiddle:

<!DOCTYPE html>
<html>

  <head>
    <link rel="stylesheet" href="style.css">
  </head>

  <body>
    <h2>Promise vs setTimeout</h2>
    <div><button id="settimeout-test">setTimeout with slow running function</button></div>
    <div><button id="promise-test">Promise and slow running function</button></div>
    <div><button id="clear">Clear Results</button></div>
    <h5>Results</h5>
    <div id="result"></div>

    <script>
        const slow = function() {
            let nu = Date.now();
            while (Date.now() - nu < 1000) {}
        }
        const getSlowPromise = () => new Promise(resolve => {
                slow();
                resolve();
        });
        const resultsElement = document.getElementById('result')
        const log = (message) => {
            resultsElement.innerText += message;
        }  

        const settimeoutButton = document.getElementById('settimeout-test');
        settimeoutButton.addEventListener('click', () => {
            const now = Date.now();
            log(`\nsetTimeout test starts after ${Date.now() - now} ms`);
            setTimeout(() => {
                slow();
                log(`\nSlow function completes after ${Date.now() - now} ms`);
            }, 0);
            log(`\nEvent listener completes after ${Date.now() - now} ms`);
        });

        const promiseButton = document.getElementById('promise-test');
        promiseButton.addEventListener('click', () => {
            const now = Date.now();
            log(`\nsetTimeout test starts after ${Date.now() - now} ms`);
            getSlowPromise().then(res => log(`\nPromise completes after ${Date.now() - now} ms`));
            log(`\nevent listener completes after ${Date.now() - now} ms`);
        })

        const clear = () => resultsElement.innerText = '';
        const clearButton = document.getElementById('clear');
        clearButton.addEventListener('click', () => clear());

    </script>

  </body>

</html>

I thought Promise and setTimeout would behave in a similar way, add the code to the task queue and then continue the execution. The order of the results are the same but the promise with a long running task seems to block the rendering until the long running task completes. Can someone explain this?

The example runs best in Chrome.

Update: I'm not trying to make the long running task to run in parallel, I just want to understand why Promise and setTimeout behave differently in my example. But if you do want to run the task in parallel then Web Workers / Worker threads is the way to go as Quentin suggests.

But the answer to my question seems to be that Promise constructors are synchronous as Bergi writes in a comment. Here is a longer explanation

Edminsson
  • 2,426
  • 20
  • 25
  • 3
    because `while (Date.now() - nu < 1000) {}` blocks rendering - Promises don't magic away synchronous tight loops like that – Bravo Oct 29 '18 at 11:15
  • 1
    `setTimeout` enqueued calls also run at a different part of the event loop than microtasks like `Promise.then` calls. While that isn't the problem with your example code, it will cause you problems if you expect them to be equivalent in all cases. – Jared Smith Oct 29 '18 at 11:19
  • 1
    [The `new Promise` constructor callback runs immediately](https://stackoverflow.com/a/29964540/1048572). You are looking for `Promise.resolve().then(slow)` to be similar to `setTimeout(slow, 0)` – Bergi Oct 29 '18 at 11:25
  • Thank you @Bergi this was the answer I was looking for. If you post it as an answer I will accept it. – Edminsson Oct 29 '18 at 12:22
  • https://stackoverflow.com/questions/28856509/how-native-javascript-promise-handles-blocking-code – Bergi Dec 19 '18 at 13:26
  • https://stackoverflow.com/questions/53876344/correct-way-to-write-a-non-blocking-function-in-node-js – Bergi Mar 15 '22 at 13:55

1 Answers1

7

while (Date.now() - nu < 1000) {} doesn't simulate a long running task that is syncronous. It is a long running task that is syncronous.

Promises are a way to manage asynchronous code, not a way to make code asynchronous, nor a way to simulate multithreading.

For that, you need to look at workers.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • You are right that it is a long running task, I just meant it simulated a more meaningful task. I think the code is asynchronous since it actually runs the last call to the log function before it runs the code inside the Promise or setTimeout in both cases. But it doesn't run in parallel of course, since javascript is single threaded. But I'm not trying to run in parallel I'm just trying to understand why Promise and setTimeout behave differently. – Edminsson Oct 29 '18 at 12:12