Is setTimeout required?

后端 未结 2 2010
谎友^
谎友^ 2021-01-23 00:58

I have a question to async, await and setTimeout(). I thought, I use asynchron functions for slow processes. So I tried it with a large loop. On my Computer, it needs few second

相关标签:
2条回答
  • 2021-01-23 01:50

    TL:DR

    Promises and async functions don't offload your code to another thread. If you want to move that long-running process off the main thread, on browsers look at web workers, and on Node.js look at child processes.

    Details

    Promises and async functions (which are just a syntax for creating and consuming promises) don't move your processing to any other thread, it still happens on the same thread you start the process on. The only thing they do is ensure that then and catch callbacks are called asynchronously. They don't make your code asynchronous (other than that one thing, ensuring the callbacks happen asynchronously).

    So your first block using setTimeout just sets a timeout, returns a promise, and then when the timeout expires it blocks the main thread while your slow-running process executes. This just changes when the blocking happens a little bit, it doesn't change the fact of the blocking.

    You can see that effect here, notice how the counter pauses when the long-running process occurs:

    function slowFunction() {
      return new Promise(resolve => {
        setTimeout(() => {
          const stop = Date.now() + 2000;
          while (Date.now() < stop) {
            // busy wait (obviously, never really do this)
          }
        }, 1000);
      });
    };
    
    console.log("before slowFunction");
    slowFunction()
      .then(() => {
        console.log("then handler on slowFunction's promise");
      })
      .catch(console.error);
    console.log("after slowFunction");
    
    let counter = 0;
    const timer = setInterval(() => {
      console.log(++counter);
    }, 100);
    setTimeout(() => {
      clearInterval(timer);
      console.log("done");
    }, 3000);
    .as-console-wrapper {
      max-height: 100% !important;
    }

    Your second block not using setTimeout just blocks right away, because the promise executor function (the function you pass new Promise) runs immediately and synchronously, and you're not doing anything to make it asynchronous.

    You can see that here; the counter pauses right away, not later:

    function slowFunction() {
      return new Promise(resolve => {
        const stop = Date.now() + 2000;
        while (Date.now() < stop) {
          // busy wait (obviously, never really do this)
        }
      });
    };
    
    console.log("before slowFunction");
    slowFunction()
      .then(() => {
        console.log("then handler on slowFunction's promise");
      })
      .catch(console.error);
    console.log("after slowFunction");
    
    let counter = 0;
    const timer = setInterval(() => {
      console.log(++counter);
    }, 100);
    setTimeout(() => {
      clearInterval(timer);
      console.log("done");
    }, 3000);
    .as-console-wrapper {
      max-height: 100% !important;
    }

    We don't even see the before slowFunction log appear until after the long-running code has finished, because the browser never got a chance to repaint, we had the thread hogged.

    Regarding async functions: The code in an async function starts out synchronous, and is synchronous until the first await (or other construct, such as setTimeout, that schedules things to execute later). Only the code after that is asynchronous (because it had to wait).

    Here's an example demonstrating that:

    async function foo() {
      console.log("before await");
      await Promise.resolve();
      console.log("after await");
    }
    
    console.log("before foo");
    foo()
      .then(() => {
        console.log("then handler on foo's promise");
      })
      .catch(console.error);
    console.log("after foo");

    Here's the output of that:

    before foo
    before await
    after foo
    after await
    then handler on foo's promise
    

    Notice how before await occurs before after foo; it's synchronous with the call to foo. But then after await doesn't occur until later (because await Promise.resolve() has to make the code following it occur asynchronously; it's syntactic sugar for then, which promises not to call its handler synchronously even if the promise is already resolved).

    0 讨论(0)
  • 2021-01-23 01:52

    The difference is that this is completely synchronous code:

    return new Promise(resolve => {
        for (let i = 0; i < 4000000000; i++) {};
        resolve('Ready at ' + new Date().toLocaleTimeString('de'));
    });
    

    This statement will block the JavaScript thread and force it to wait until all of those 4 billion iterations have taken place. Then it will move on to the next statement. Since the console.log executes after this, it will not execute until that loop has finished.

    That's why you are seeing a difference.

    0 讨论(0)
提交回复
热议问题