Async/Await Implicitly Returns Promise

async/await implicitly returns promise?

The return value will always be a promise. If you don't explicitly return a promise, the value you return will automatically be wrapped in a promise.

async function increment(num) {
return num + 1;
}

// Even though you returned a number, the value is
// automatically wrapped in a promise, so we call
// `then` on it to access the returned value.
//
// Logs: 4
increment(3).then(num => console.log(num));

Same thing even if there's no return! (Promise { undefined } is returned)

async function increment(num) {}

Same thing even if there's an await.

function defer(callback) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve(callback());
}, 1000);
});
}

async function incrementTwice(num) {
const numPlus1 = await defer(() => num + 1);
return numPlus1 + 1;
}

// Logs: 5
incrementTwice(3).then(num => console.log(num));

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));


In my synopsis the behavior is indeed inconsistent with traditional
return statements. It appears that when you explicitly return a
non-promise value from an async function, it will force wrap it in a
promise. I don't have a big problem with it, but it does defy normal
JS.

ES6 has functions which don't return exactly the same value as the return. These functions are called generators.

function* foo() {
return 'test';
}

// Logs an object.
console.log(foo());

// Logs 'test'.
console.log(foo().next().value);

Returning promise from async function

Yes, but there's no reason to. async functions always return promises. Just handle that logic inline, and throw customErr to reject the function's promise:

async function beforeProcessing(ctx) {
let res;
try {
res = await validateList(ctx)
} catch (error) {
return new Error(error); // *** This is highly suspect
}
if (res && res.length > 0) {
const customErr = new Error(`missing for ${res} types`);
customErr.statusCode = 422;
throw customErr;
}
}

But as I mentioned in a comment above, returning an error object is highly suspect, it fulfills the promise with the error rather than rejecting the promise. Just let the error propagate to the caller (which will reject the async function's promise):

async function beforeProcessing(ctx) {
let res = await validateList(ctx)
if (res && res.length > 0) {
const customErr = new Error(`missing for ${res} types`);
customErr.statusCode = 422;
throw customErr;
}
}

Also, that if condition looks odd vs. the error message. The error message makes it look like the condition should be <= 0, not > 0, but of course I could be inferring incorrectly there.

Async function returning promise, instead of value

An async function always returns a promise. That's how it reports the completion of its asynchronous work. If you're using it in another async function, you can use await to wait for its promise to settle, but in a non-async function (often at the top level or in an event handler), you have to use the promise directly, e.g.:

latestTime()
.then(time => {
console.log(time);
})
.catch(error => {
// Handle/report error
});

If you're doing this at the top level of a JavaScript module, some environments now support the upcoming top-level await in modules:

const time = await latestTime();

JavaScript engines are getting support for top-level await, and Webpack has experimental support for it, for instance.


Here's a rough translation of your async function in explicit Promise terms:

function latestTime() {
return new Promise((resolve, reject) => {
web3.eth.getBlock('latest')
.then(bl => {
console.log(bl.timestamp);
console.log(typeof bl.timestamp.then == 'function');
resolve(bl.timestamp);
})
.catch(reject);
});
}

Some important notes on that:

  • The function you pass to new Promise (the promise executor function) gets called synchronously by new Promise.
    • Which is why the operation starts, web3.eth.getBlock is called synchronously to start the work.
  • Any error (etc.) thrown within the promise executor gets caught by new Promise and converting into a promise rejection.
  • Any error (etc.) thrown within a promise callback (like the one we're passing then) will get caught and converted into a rejection.

Is there an implicit return value to async functions

There is no implicit return, but any returned value is wrapped in a promise.

Is it possible for an 'await' to resolve to a Promise?

No, it's impossible for an await expression to result in a promise.

Just like you cannot fulfill a promise with another promise, and like then() never calling the fulfillment handler with a promise.

async/await always returns promise

Every async function returns a Promise object. The await statement operates on a Promise, waiting until the Promise resolves or rejects.

So no, you can't do console.log on the result of an async function directly, even if you use await. Using await will make your function wait and then return a Promise which resolves immediately, but it won't unwrap the Promise for you. You still need to unwrap the Promise returned by the async function, either using await or using .then().

When you use .then() instead of console.logging directly, the .then() method makes the result of the Promise available to you. But you can't get the result of the Promise from outside the Promise. That's part of the model of working with Promises.



Related Topics



Leave a reply



Submit