2

I have a very simple code snippet like this

async function neverResolve() {
  return new Promise(() => {
    console.log("This promise will never resolve");
  });
}

(async () => {
  try {
    console.log("START");
    // neverResolve().then().catch(); // uncommenting this line works as expected
    await neverResolve();
    await new Promise((resolve) => setTimeout(() => resolve(), 5000));
    console.log("END");
  } catch (error) {
    console.log("ERR: ", error);
  }
})();

Why the above function doesn't wait for 5 second and print's the END. It automatically terminates after printing

START
This promise will never resolve

But if we execute the same function but with a .then() construct, I get the expected result.

async function neverResolve() {
  return new Promise(() => {
    console.log("This promise will never resolve");
  });
}

(async () => {
  try {
    console.log("START");
    neverResolve().then().catch(); 
    await new Promise((resolve) => setTimeout(() => resolve(), 5000));
    console.log("END");
  } catch (error) {
    console.log("ERR: ", error);
  }
})();
Anonymous Zombie
  • 829
  • 2
  • 7
  • 14
  • Try removing the `async` keyword in front of the definition of `neverResolves`. Otherwise you're wrapping it in two promises, which is not what you meant to do. – Guillaume Brunerie Feb 06 '23 at 20:31
  • 5
    `neverResolve().then().catch();` doesn't wait. It's a fire and forget. Therefore, the code will continue past it. The first one never resolves and eventually the process is killed. No real reason to keep it around forever. – VLAZ Feb 06 '23 at 20:31
  • 3
    @GuillaumeBrunerie doesn't really matter. With `async function` the promise from `neverResolves()` will assume the state of the promise returned. So, it's just the same non-resolving promise at the end. – VLAZ Feb 06 '23 at 20:32
  • 1
    I add some general advice: `async/await` is just syntactical sugar. When you are experiencing a different behavior in a code that is just syntactical sugar, you can just paste it in BabelJS and see how it gets converted – Christian Vincenzo Traina Feb 06 '23 at 20:37
  • `await new Promise(() => {});` is evil – Shankar Regmi Feb 06 '23 at 21:24

5 Answers5

1

neverResolve neither resolves or rejects, so the program hangs indefinitely at the await. Consider abstracting the timeout functionality in its own generic function, timeout -

const sleep = ms =>
  new Promise(r => setTimeout(r, ms));

const timeout = (p, ms) =>
  Promise.race([
    p,
    sleep(ms).then(() => { throw Error("timeout") })
  ]);
  
const neverResolve = () => new Promise(() => {});

(async function() {
  try {
    console.log("connecting...");
    await timeout(neverResolve(), 2000);
  }
  catch (err) {
    console.error(err);
  }
})();
Mulan
  • 129,518
  • 31
  • 228
  • 259
1

This is what's happening in your case:

  1. Your program starts it's execution from an anonymous IIFE async function, as this is an async function, it immediately returns a Promise to a global scope.So the execution of anonymous IIFE is deferred . You can easily validate this by adding a console.log("OK"); at the end of your IIFE invocation, which is printed to the console

  2. Node keeps a reference count of things like timers and network requests. When you make a network, or other async request, set a timer, etc. Node adds on to this ref count. When the times/request resolve Node subtracts from the count. Ref. video link So what happens inside your IIFE is:

    • console.log("START"); <--- gets printed to console
    • await neverResolve(); Here things get's interesting, this await call will defer the execution and blocks until the callback are executed, either resolve or reject.

    But in this case there are no callbacks registered and nodejs will think that it finished processing all the request and will terminate the process.

Shankar Regmi
  • 854
  • 1
  • 7
  • 16
0

In the first example await neverResolve(); waits forever and never resolves as stated. Javascript can go off and do other things in other tasks while waiting for this.

In the second example by adding .then() you've told javascript to continue processing code below. Typically, all the code below would either be inside the callback for then() or catch() which would then create the same pause you're seeing.

There are very deliberate reasons for these nuances that allow you to send a fetch request, do other work and then come back to see if the fetch is done later. See my comments in this marked up and slightly modified example.

async function slowFetch() {
  return new Promise((resolve) => {
    // a fetch statement that takes forever
    fetch().then(data => resolve(data));
  });
}

(async () => {
  try {
    console.log("START");

    // start the slow fetch right away
    const dataPromise = slowFetch();

    // do some other tasks that don't take as long
    await new Promise((resolve) => setTimeout(() => resolve(), 5000));

    // wait for the data to arrive
    const data = await dataPromise;

    // do something with the data like fill in the page.

    console.log("END");
  } catch (error) {
    console.log("ERR: ", error);
  }
})();
NetByMatt
  • 703
  • 4
  • 13
  • The OP is asking about why returning a Promise that never calls `resolve()` doesn't executes the code block after await it, but in your example, you are doing a `resolve() inside a `slowFetch()`` – Shankar Regmi Feb 06 '23 at 20:39
  • `new Promise((resolve) => { fetch().then(data => resolve(data));});` is an exceptionally bad example as it just encourages the pointless *and dangerous* [explicit promise construction antipattern](https://stackoverflow.com/q/23803743). The one in this code never handles the rejected promise of `fetch()` thus runs a real risk of becoming unresolvable. – VLAZ Feb 06 '23 at 20:40
  • Yes, I answer that in my first sentence, then contrast it with how the second examples works and continues processing. Finally I tried to pull together an example of why you would potentially not immediately await a promise which straddles both both examples and the original question. – NetByMatt Feb 06 '23 at 20:42
0

By doing neverResolve().then().catch(); what you actually did is;

async function neverResolve() {
  return new Promise(() => {
    console.log("This promise will never resolve");
  });
}

(async () => {
  try {
    console.log("START");
    (async function(){
       try {
         await neverResolve()
       }
       catch{
       }
     })(); 
    await new Promise((resolve) => setTimeout(() => resolve(), 5000));
    console.log("END");
  } catch (error) {
    console.log("ERR: ", error);
  }
})();

The inner async IIFE runs just like neverResolve().then().catch(); does. You should not mix promises with async await abstraction.

Redu
  • 25,060
  • 6
  • 56
  • 76
0

Why the above function doesn't wait for 5 second and print's the END. It automatically terminates after printing

Because your code never gets past this:

await neverResolve();

Since neverResolve() returns a promise that never resolves or rejects, this function is forever suspended at that line and the lines of code after this statement in the function never execute.

await means that the function execution should be suspended indefinitely until the promise you are awaiting either resolves or rejects.


But if we execute the same function but with a .then() construct, I get the expected result.

When you change the await to this:

neverResolve().then().catch(); 

The function execute is NOT suspended at all. It executes neverResolve(). That returns a promise which it then calls .then() on. That call to .then() just registers a callback with the promise (to be called later when the promise resolves). That returns another promise which it then calls .catch() on which just registers a callback with the promise (to be called later if/when the promise rejects).

Now, you aren't even passing a callback in either case, so those .then() and .catch() have nothing to actually do, but even if you did pass a callback to each of them, then they would just register that callback and immediately return. .then() and .catch() are not blocking. They just register a callback and immediately return. So, after they return, then next lines of code in the function will execute and you will get the output you were expecting.


Summary

await suspends execution of the function until the promise you are awaiting resolves or rejects.

.then() and .catch() just register callbacks for some future promise state change. They do not block. They do not suspend execution of the function. They register a callback and immediately return.

jfriend00
  • 683,504
  • 96
  • 985
  • 979