Post cover

'return await promise' vs 'return promise' in JavaScript

Posted August 10, 2021

When returning from a promise from an asynchronous function, you can wait for that promise to resolve return await promise, or you can return it directly return promise:


async function func1() {
const promise = asyncOperation();
return await promise;
}
// vs
async function func2() {
const promise = asyncOperation();
return promise;
}

You'll see shortly that both expressions do work.

However, are there cases when these expressions behave differently? Let's find out!

1. Same behavior

To find the difference between the 2 expressions (return await promise vs return promise), I'm going to use a helper function delayedDivide(n1, n2).

The function divides 2 numbers, and returns the division result wrapped in a promise:


function promisedDivision(n1, n2) {
if (n2 === 0) {
return Promise.reject(new Error('Cannot divide by 0'));
} else {
return Promise.resolve(n1 / n2);
}
}

If the second (divisor) argument is 0, the function returns a rejected promise because division by 0 is not possible.

Ok, having the helper function defined, let's divide some numbers.

The following function divideWithAwait() uses return await promisedDivision(6, 2) expression to return the division of 6 by 2 wrapped in a promise:


async function divideWithAwait() {
return await promisedDivision(6, 2);
}
async function run() {
const result = await divideWithAwait();
console.log(result); // logs 3
}
run();

Open the demo.

Inside the run() function the await divideWithAwait() expression evaluates to the division result 3. All good.

Now let's try to use the second expression without the await keyword, and return directly the promise wrapping the division result return promisedDivision(6, 2):


async function divideWithoutAwait() {
return promisedDivision(6, 2);
}
async function run() {
const result = await divideWithoutAwait();
console.log(result); // logs 3
}
run();

Open the demo.

Even without using the await keyword inside divideWithoutAwait(), the expression await divideWithoutAwait() inside the run() function still evaluates correctly to the 6 / 2 division as 3!

At this step, you have seen that using return await promise and return promise don't differ. At least when dealing with successfully fulfilled promises.

But let's search more!

2. Different behavior

Now let's take another approach, particularly try to work with rejected promises. To make the function promisedDivision(n1, n2) return a rejected promise let's set the second argument to 0.

Because promisedDivision(n1, 0) now would return rejected promises, let's also wrap the invocation into a try {... } catch (error) {...} — to see whether the rejected promise is caught.

Ok, let's use return await promisedDivision(5, 0) expression with the await keyword:


async function divideWithAwait() {
try {
return await promisedDivision(5, 0);
} catch (error) {
// Rejection caught
console.log(error); // logs Error('Cannot divide by 0')
}
}
async function run() {
const result = await divideWithAwait();
}
run();

Open the demo.

Because the division by zero is not possible, promisedDivision(5, 0) returns a rejected promise. The catch(error) { ... } successfully catches the rejected promise thrown by promisedDivision(5, 0).

What about the second approach, where the await is omitted?


async function divideWithoutAwait() {
try {
return promisedDivision(5, 0);
} catch (error) {
// Rejection NOT caught
console.log(error);
}
}
async function run() {
const result = await divideWithoutAwait();
}
run(); // Uncaught Error: Cannot divide by 0

Open the demo.

This time, however, catch(error) { ... } doesn't catch the rejected promise.

Now you can easily see the main difference between using return await promise and return promise:

When being wrapped into try { ... }, the nearby catch(error) { ... } catches the rejected promise only if the promise is awaited (which is true for return await promise).

3. Conclusion

In most situations, especially if the promises successfully resolve, there isn't a big difference between using return await promise and return promise.

However, if you want to catch the rejected promise you're returning from an asynchronous function, then you should definitely use return await promise expression and add deliberately the await.

catch(error) {...} statement catches only awaited rejected promises in try {...} statement.

Like the post? Please share!

Dmitri Pavlutin

About Dmitri Pavlutin

Software developer and sometimes writer. My daily routine consists of (but not limited to) drinking coffee, coding, writing, overcoming boredom 😉. Living in the sunny Barcelona. 🇪🇸