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:
If a function is declared with the
asynckeyword, we can call it with theawaitkeyword. So that's like snippet 4 (declaregetPromisewithasync) and snippet 1 (calling withawait). There should be no surprise here.But if we declare
getPromisewithout theasynckeyword (snippet 3), we can still call it with theawaitkeyword. The reason being isgetpromise()returns aPromise object. If a function returns a Promise, we can call it withawait. So snippet 3 and snippet 1 still work together.What about calling using the
thenclause? Of course, beforeasync/await, back when we only had Promise, we declare a function without the async keyword (snippet 3) and we call it with thethenclause (snippet 2). That was the way to do then. It remains possible to do so now. Again no surprise here.So can we declare
getPromise()with the async keyword (snippet 4) and call it with thethenclause (snippet 2) ? Yes, that works too.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);
}
})();
Top comments (1)
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.