skip to Main Content

I know that all functions marked with async return a Promise object, even if it’s not returned explicitly. So I made the following experiment:

async function getPromise() {
    setTimeout(() => { return "value"; }, 2000);
}

async function implicitPromiseTest() {
    const val = await getPromise();
    console.log(val);             // prints "undefined"
}

implicitPromiseTest();    

In example 1, the value after return should be implicitly wrapped in a Promise. This Promise would then be returned from the function. I expected it to be resolved in the implicitPromiseTest function and the returned value – printed to console.

I guess this doesn’t work because the implicit Promise from getPromise never actually resolves. As I understand it, the proper way to do this task seems to be:

async function getPromise() {
    return new Promise((resolve, reject) => 
      { setTimeout(() => { resolve("value"); }, 2000);})
}

async function implicitPromiseTest() {
    const val = await getPromise();
    console.log(val);             // prints "value"
}

But this raises questions for me:

  1. How can I resolve a Promise returned implicitly from an async function, without explicitly returning a Promise from that function’s block? Such a feature would allow me to simplify my code substantially.

  2. If this can’t be done, then what even is the point of async always implicitly returning Promises? What’s the use of those Promises if they can’t be resolved or rejected?

  3. In my 2nd function, the await keyword should halt function execution until the Promise from getPromise() is resolved. And since it never resolves, the execution should stop there indefinitely. Then why does the console.log execute?

2

Answers


  1. In example 1, the value after "return" should be implicitly wrapped in a Promise.

    It’s only going to wrap a promise around things you return from the async function. A value returned in a setTimeout callback function is going to be ignored.

    So what’s happening is: your async function starts running, then sets up a timeout, then implicitly returns undefined. Since this is an async function, that undefined gets wrapped in a promise. That promise is in a resolved state, since the function is done executing. Some time later the timeout goes off, but that has no effect on the async function which has already finished resolving its promise.

    async/await exists to simplify the code for working with already-existing promises. It does not take non-promise code and convert it to promise code, except for the case where you return a non-promise. If you want to create a promise from setTimeout or any other callback-based function, then you must use the new Promise constructor.

    what even is the point of async always implicitly returning Promises?

    So you don’t have to end all your async functions with return Promise.resolve(someValue). You can instead do return someValue

    eg:

    async function fetchStuff() {
      const response = await fetch('someurl');
      const data = await response.json();
      return data.userList; // instead of return Promise.resolve(data.userList)
    }
    

    In my 2nd function, the "await" keyword should halt function execution until the Promise from "getPromise()" is resolved. And since it never resolves […]

    That’s a false premise. In your first example the promise resolves right away, in your second example it resolves when the timeout goes off. Now that it’s resolved, any code that was awaiting it can continue

    Login or Signup to reply.
  2. It’s important to remember that async/ await is just a different way of writing asynchronous code, and implemented as normal Promises under the hood. These keywords can turn Promise chains or nested promises into neater, easier to read sequential statements that look more like the rest of your synchronous code.

    Let’s break down the parts of your first example, since there are some things here that seem to be causing some confusion.

    Default return value of async function

    As you note, async functions always return promises.

    This is true even when the function has no return– in this case, a Promise which resolves to the value undefined will be returned; there won’t be anything for the caller to "wait for" in that case, since the returned Promise will already be settled, and can resolve to undefined immediately on return.

    function syncFunc() {
      console.log('I return nothing');
    }
    
    async function asyncFunc() {
      console.log('I return nothing, but wrapped in a Promise');
    }
    
    console.log('syncFunc:', syncFunc());
    asyncFunc().then(val => console.log('asyncFunc:', val));

    setTimeout

    Your example seems to treat setTimeout as asynchronous, but it’s not async in the Promise sense– code that calls setTimeout doesn’t wait for it to fire before continuing on.

    This means that getPromise() calls setTimeout and then immediately returns undefined thereafter. Making the function async just changes how that undefined is wrapped in the return to its caller, and doesn’t at all change how setTimeout is treated.

    This is more obvious if we log from setTimeout‘s callback; it fires well after getPromise is already done.

    async function getPromise() {
      setTimeout(() => console.log('setTimeout callback fired!'), 2000);
    }
    
    async function implicitPromiseTest() {
      console.log('Calling getPromise');
      await getPromise();
      console.log('getPromise finished');
    }
    
    implicitPromiseTest();    

    await

    await unwraps/ resolves expressions; if you await a Promise, you get the fulfillment value of that Promise (once it finishes), and if you await a non-Promise, you get the value directly (e.g. await 3 just gives you 3).

    In your code, by calling await getPromise(), you’re resolving the Promise that’s returned by the async function; and since getPromise has no explicit return, it returns undefined, which is then what the Promise fulfills with, and what await unwraps & returns.

    If you call getPromise() without await, you’ll get the bare promise, which you can then manually chain .then on.

    async function getPromise() {
        setTimeout(() => { return "value"; }, 2000);
    }
    
    async function implicitPromiseTest() {
            const promise = getPromise();
            promise.then(val => console.log("I'm resolved with the value:", val));
    }
    
    implicitPromiseTest();    

    How can I resolve a Promise returned implicitly from an async function, without explicitly returning a Promise from that function’s block?

    You’re already doing this with await getPromise()! It just doesn’t feel like it because of the confusions explained above.

    If this can’t be done, then what even is the point of async always implicitly returning Promises? What’s the use of those Promises if they can’t be resolved or rejected?

    They can and they are; you’re just awaiting it immediately, which means you never see the Promise itself.

    Your code is effectively doing await Promise.resolve(undefined), which simply yields undefined.

    In my 2nd function, the "await" keyword should halt function execution until the Promise from "getPromise()" is resolved. And since it never resolves, the execution should stop there indefinitely. Then why does the "console.log" execute?

    Because the Promise does resolve, fulfilled with the return value of getPromise, which is undefined.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search