1
async () => {
try {
  setTimeout(() => console.log('next task'), 0);
  await timeTakingFunc();
  console.log('after waiting for timetaking function');
} catch (error) {
  console.log(error.message);
}})();

OUTPUT:
after waiting for timetaking function
next task


shouldn't 'next task' be printed first as timeTakingFunction(); is taking long time to execute and timeout is only 0ms?

the time taking function:

async function timeTakingFunc() {
return new Promise((resolve, error) => {
    for (let t = 1; t < 100000; t++) {
      for (let k = 1; k < 100000; k++) {
      }
    }
    resolve();
  });
};
  • 3
    Please show `timeTakingFunc`. – AKX Feb 16 '22 at 10:55
  • 3
    This should help: [Difference between microtask and macrotask within an event loop context](https://stackoverflow.com/q/25915634/218196). More information: https://html.spec.whatwg.org/multipage/webappapis.html#event-loops, https://html.spec.whatwg.org/multipage/webappapis.html#hostenqueuepromisejob, – Felix Kling Feb 16 '22 at 10:57
  • ```async function timeTakingFunc() { return new Promise((resolve, error) => { for (let t = 1; t < 100000; t++) { for (let k = 1; k < 100000; k++) { } } resolve(); }); };``` – Akash Dubey Feb 16 '22 at 11:10
  • 1
    @AkashDubey - I've updated my answer to explain why that function does its work synchronously – T.J. Crowder Feb 16 '22 at 11:23
  • 2
    @AkashDubey: The callback function passed to `Promise` is executed synchronously. – Felix Kling Feb 16 '22 at 11:24

1 Answers1

3

Your timeTakingFunc does all of its work synchronously, for two reasons:

  1. An async function's code is synchronous up until the first await or return. That's so it can start whatever process it's going to complete asynchronously.
  2. The executor function you pass to new Promise is run synchronously, running to completion before the new promise is returned to the code making the call — for the same reason that an async function starts out synchronous. (In fact, the beginning of an async function, before the first await or return, is directly analogous to the body of a promise executor function.)

So there's nothing actually asynchronous about that function.

(Side note: There's no reason to use async on a function that never uses await and that explicitly creates its promise. It's redundant.)

If the function had any actual asynchronous work in it that took virtually any time to complete, you'd see the order of output you expect:

async function timeTakingFunc() {
    // This fetch fails, but it takes non-zero time to do so
    try {
        const response = await fetch("https://via.placeholder.com/150");
        if (response.ok) {
            await response.arrayBuffer();
        }
    } catch (e) {
    }
}
(async () => {
    try {
        setTimeout(() => console.log('next task'), 0);
        await timeTakingFunc();
        console.log('after waiting for timetaking function');
    } catch (error) {
        console.log(error.message);
    }
})();

But with that code, timeTakingFunc is synchronous:

async function timeTakingFunc() {
    const p = new Promise((resolve, error) => {
        console.log("Start of executor function");
        for (let t = 1; t < 100000; t++) {
            for (let k = 1; k < 100000; k++) {}
        }
        console.log("End of executor function");
        resolve();
    });
    console.log("timeTakingFunc returning promise");
    return p;
};
(async () => {
    try {
        setTimeout(() => console.log('next task'), 0);
        await timeTakingFunc();
        console.log('after waiting for timetaking function');
    } catch (error) {
        console.log(error.message);
    }
})();

...and the JavaScript engine does this:

  1. Sets the timer (which at some point will queue a task to run the timer callback)
  2. Runs the synchronous part of timeTakingFunction (which is all of it) to completion
  3. [Because of the await] Queues a microtask in the current task's microtask queue to run the remainder of the code
  4. Reaches the end of the current task
  5. Processes the microtask queue
    • Runs the remainder of the code, logging "after waiting for timetaking function"
  6. Processes the next task in the task queue, which might be the timer callback (if not, it'll show up in the queue fairly soon)
    • When the timer task is run, it logs "next task"

Which is why you see the order you see, with a synchronous function.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    To be entirely pedantic, there is some rare case where you could still have an "asynchronous" `timeTakingFunc` and the result seen by OP. All it takes is a task with more priority than the timeout-queue, or a callback: https://jsfiddle.net/xbhLj3pt/ And more realistically it could also simply use branches where all maybe async functions do return already resolved promises, less "asynchronous" in my mind, but possible... – Kaiido Feb 16 '22 at 11:18