Trying to understand between new Promise and Async/await

Viewed 116

I have a very basic question:

This code

function resolveAfter2Seconds() {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  });
}

async function asyncCall() {
  console.log('calling');
  const result = await resolveAfter2Seconds();
  console.log(result);
  // expected output: "resolved"
}

asyncCall();

gives this output, which is correct:

> "calling"
> "resolved"

This one however:

async function resolveAfter2Seconds() {
    setTimeout(() => {
      return 'resolved';
    }, 2000);
}

async function asyncCall() {
  console.log('calling');
  const result = await resolveAfter2Seconds();
  console.log(result);
  // expected output: "resolved"
}

asyncCall();

It gives instead:

> "calling"
> undefined

From javascript.info:

The word “async” before a function means one simple thing: a function always returns a promise. Other values are wrapped in a resolved promise automatically. So, async ensures that the function returns a promise, and wraps non-promises in it

So there's something I am misunderstanding, isn't the resolveAfter2Seconds returning a promise in both scenarios (the resolveAfter2Seconds in the 2nd scenario has async)? If so why the output in the 2nd scenario?

3 Answers

In the second case, you are not returning anything from the resolveAfter2Seconds function, the return in the code is for the callback of the setTimeout, which will not be returned by the resolveAfter2Seconds function. Hence the undefined.

Its a pretty basic beginner mistake you use return in an callback and expect to recevive something from the parent function

This here:

async function resolveAfter2Seconds() {
    setTimeout(() => {
      return 'resolved';
    }, 2000);
}

Can be written to this here:

async function resolveAfter2Seconds() {
    setTimeout(function() {
      return 'resolved';
    }, 2000);
}

And as you can see there is a second function and you return resolved to your callback function. There is actually no way to return this value like you want to, you will need to use Promises like in your first example

Since resolveAfter2Seconds doesnt return anything it will return by default undefined

No return = returns undefined

Both functions are returning a promise, the main differences between the two are when each promise resolves and what each promise resolves to. In your second function resolveAfter2Seconds, you're only returning inside of the setTimeout function, and as a result, you return back to the caller of the setTimeout callback function, not your resolveAfter2Seconds. Rather, your resolveAfter2Seconds doesn't return anything, and so it will implicitly return undefined which gets wrapped within a promise. It is more or less the same as writing:

function resolveAfter2Seconds() {
    setTimeout(() => {
      return 'resolved';
    }, 2000);
      
    return Promise.resolve(undefined);
}

function resolveAfter2Seconds() {
  setTimeout(() => {
    return 'resolved';
  }, 2000);

  return Promise.resolve(undefined); // returns  a promise which resolves with the value of `undefined`
}

async function asyncCall() {
  console.log('calling');
  const result = await resolveAfter2Seconds();
  console.log(result);
  // expected output: "resolved"
}

asyncCall();

The above returns a Promise which resolves immediately with the value of undefined. As a result, you either need to explicitly wrap your setTimeout in a promise and resolve() (like your first resolveAfter2Seconds), or use something like a helper sleep method:

const sleep = n => new Promise(res => setTimeout(res, n));
async function resolveAfter2Seconds() {
  await sleep(2000);
  return "resolved";
}

async function asyncCall() {
  console.log('calling');
  const result = await resolveAfter2Seconds();
  console.log(result);
  // expected output: "resolved"
}

asyncCall();

Related