loading...

Async without await, Await without async

codeprototype profile image Kevin Le ・2 min read

Let's say we have a function getPromise() that returns a Promise that will resolve to some value in the future. The question is whether we need to declare it with the async keyword if we want to call it either (i) using the async/await style or (ii) using the then clause. For example, in the same code base, some time we want to call it like this:

//Snippet 1. Call using async/await
(async() => {
    try {
        const result = await getPromise("a");   
        console.log('async/await -> ', result);
    } catch (err) {
        console.log(err);
    }
})();

...and in some other time, like this:

//Snippet 2. Call using then clause
(() => {
    getPromise("b")
    .then(result => {
        console.log('then -> ', result);
    })
    .catch(err => {
        console.log(err);
    });
})();

then should we declare getPromise() like this?

//Snippet 3. Declare without async keyword
const getPromise = (s) => {  
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(s), 500);
    });
}

...or like this?

//Snippet 4. Declare with async keyword
const getPromise = async (s) => {  
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(s), 500);
    });
}

It turns out all of the above combinations are possible, because async/await is still Promise-based under the hood:

  1. If a function is declared with the async keyword, we can call it with the await keyword. So that's like snippet 4 (declare getPromise with async) and snippet 1 (calling with await). There should be no surprise here.

  2. But if we declare getPromise without the async keyword (snippet 3), we can still call it with the await keyword. The reason being is getpromise() returns a Promise object. If a function returns a Promise, we can call it with await. So snippet 3 and snippet 1 still work together.

  3. What about calling using the then clause? Of course, before async/await, back when we only had Promise, we declare a function without the async keyword (snippet 3) and we call it with the then clause (snippet 2). That was the way to do then. It remains possible to do so now. Again no surprise here.

  4. So can we declare getPromise() with the async keyword (snippet 4) and call it with the then clause (snippet 2) ? Yes, that works too.

  5. What's not to do is the following case. The outer function which is the caller function is declared without the async keyword. The will cause a run-time error.

const getPromise = async (s) => {    
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(s), 500);
    });
}

//
(() => { //no async keyword here
  try {
    const result = await getPromise("a");   
    console.log('async/await -> ', result);
  } catch (err) {
    console.log(err);
  }
})();

6... but the following is OK (which is why I title the article async without await, because there's no await anywhere in here):

const getPromise = async (s) => {    
   return new Promise((resolve, reject) => {
     setTimeout(() => resolve(s), 500);
   });
}
(async() => {
    getPromise("b")
    .then(result => {
        console.log('then -> ', result);
    })
    .catch(err => {
    console.log(err);
    });
})();

7... and lastly, we call getPromise() without the await keyword. In this case, result has not yet been unwrapped, so it's still only a Promise object.

const getPromise = async (s) => {    
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(s), 500);
  });
}

(async() => {
    try {
        const result = getPromise("a"); //no await, result has not been unwrapped   
        console.log('async/await -> ', result);
    } catch (err) {
        console.log(err);
    }
})();

Posted on by:

codeprototype profile

Kevin Le

@codeprototype

Driven by passion and patience

Discussion

markdown guide
 

This is a nice article Kevin.
Wanted to add little more about async function to complement with this article.

The async function automatically wraps the return value inside a promise. Whether you return a plain JS object or a Promise, it will be wrapped with a promise. Note: The compiler automatically flattens any nesting of promise for you.

In short if you are returning a promise in an async function, you can even let go of async and it will still work fine.