Prompting ChatGPT:
I think the following is a strange behavior in JavaScript:
Promises auto-unwrap, so if you do return a promise for a value from within an async function, you will receive a promise for the value (not a promise for a promise for the value).
function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback());
}, 1000);
});
}
async function increment(num) {
// It doesn't matter whether you put an await here.
return defer(() => num + 1);
}
// Logs: 4
increment(3).then(num => console.log(num));
ChatGPT
Yes, this behavior might seem strange at first, but it's actually a key feature of how async functions and Promises work in JavaScript:
When you return a Promise inside an async function, JavaScript automatically unwraps it.
That means:
- If an
asyncfunction returns a non-Promise value, it gets wrapped inPromise.resolve(). - If an
asyncfunction returns aPromise, JavaScript automatically flattens it.
function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback()); // Resolving with num + 1
}, 1000);
});
}
async function increment(num) {
// `defer()` returns a Promise.
// Since `increment` is `async`, returning a Promise means it is auto-unwrapped.
return defer(() => num + 1);
}
// Call the function and log the result
increment(3).then(num => console.log(num)); // Logs: 4-
defer()returns aPromisedefer(() => num + 1)creates a delayed Promise that resolves tonum + 1after 1 second.
-
The
async function increment(num)returns thatPromise- Because
incrementisasync, it does not return a Promise inside another Promise (no nesting). - Instead, JavaScript unwraps the returned
Promiseautomatically.
- Because
-
When calling
increment(3)- It immediately returns a
Promisethat resolves to4, not aPromise<Promise<number>>.
- It immediately returns a
-
.then(num => console.log(num))receives4, not another Promise.
If increment were a normal function:
function increment(num) {
return defer(() => num + 1);
}Then calling increment(3) still returns a Promise, and .then(console.log) still logs 4,
but the behavior is clearer because there's no async function involved.
If you explicitly await the Promise before returning, the result remains the same:
async function increment(num) {
return await defer(() => num + 1);
}- Here,
awaitpauses execution until the Promise resolves. - But since
await defer(...)already givesnum + 1, thereturnis just wrapping it in aPromiseagain.
So await here makes no difference!
β
Async functions auto-unwrap Promises
β
Returning a Promise from an async functiondoes not create a nested Promise** β
**Explicitawait` before returning a Promise is unnecessary
This auto-unwrapping makes async functions easier to work with, avoiding extra .then() chains or Promise<Promise<T>> situations.
Did you find this behavior surprising? π
https://stackoverflow.com/a/35302535/25118705