Async/Await Always Returns 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.

Async/await always returns a promise or undefined

async functions always return a promise. You're not returning anything from your map callbacks, so since the callbacks are async functions, they return promises that are fulfilled with undefined.

If you want to wait for all of the promises from your map operations to be fulfilled, use Promise.all on the result. Promise.all returns a promise that will be fulfilled with an array of the fulfillment values of the promises you give it (if they're all fulfilled) or rejected the first time any of them rejects. You wait for that promise either with await (in an async function) or by calling .then/.catch on it.

Here's an example that assumes the code where you're doing that map is not in an async function:

// If not in an `async` function
Promise.all(orderData.map(async product => {
const productId = product.id.slice(0, product.id.length - 5);
const targetProduct = await dataSources.ProductAPI.product(productId);
const name = targetProduct.name;
const quantity = 218327;

const orderItem await dataSources.OrderItemAPI.createOrderItem(
orderId,
productId,
name,
quantity,
{ ttlInSeconds: 60 * 20 }
);
return await dataSources.TransactionAPI.findProductFromOrderItem(orderItem.productId);
}))
.then(productsCorrespondingToOrderItems => {
// ...use `productsCorrespondingToOrderItems` here...
})
.catch(error => {
// ...handle/report error...
});

Note that that does the work in parallel. If you want to do it in series instead, use an async function and a for-of loop.

Why Does Async Always Return a Promise?

I thought i'd answer this primarily because async in Javascript used to confuse the hell out of me, and all of a sudden it snapped, so i hope this analogy may help this happen for you.

You have an async event. This could be anything, getting something from a server, doing something in the browser that takes time, training a machine learning model (!), executing a function or method that uses a setTimeout etc.

The beauty of Javascript and a key reason it works so well for the browser is that it uses the processor thread it runs on in a very clever way that stops the thread from getting blocked by processes that take time (like the ones mentioned above)

Many other languages, for example Ruby run on more than one thread. It is possible to use service workers to run processes on multiple threads in javascript but that is outside the scope of this answer!

The async nature of the JS event loop allows the thread to 'go off' and do something else while it is waiting for a process to finish.

The problem with this from a programming point of view is that it is possible for something in the code that relies on the result of a blocking event to get 'undefined' as a result of the event if it doesn't wait for the event to finish before it tries to use the result of it. Take this piece of code below

let scopedVariable
console.log('the code has started')
setTimeout(() => {
scopedVariable="I am the result of some async process"
}, 5000);
console.log(scopedVariable)

When the code reaches the console log, the setTimeout hasn't yet completed. As the setTimeout sets the scopedVariable only when it completes, the variable is undefined when we log it

if however

We wrap the timeout in a promise we can await it's resolve callback (first argument of promise) and the code will 'pause' until the promise reaches the resolve callback before continuing.

When we await the promise and the setTimeout completes, the resolve function sets the variable, so that when we console log it it holds the value from the promise

let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
const container = async () => {
const result = await asyncEvent
console.log(scopedVariable)
}

container()

You can use await and .then interchangably

For example we could go:

let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})
const container = async () => {
asyncEvent.then(() => console.log(scopedVariable))
}

container()

once again the code will pause at .then and then continue when the asyncEvent promise has resolved.

In fact if we use .then we don't need to enclose it in an async function so we can rewrite it like this

let scopedVariable
const asyncEvent = new Promise ((resolve,fail) => {
setTimeout(() => {
resolve(scopedVariable="I have resolved")
}, 5000);
})

asyncEvent.then(() => console.log(scopedVariable))

The great thing about .then is that the accompanying .catch allows you to catch any errors thrown by the async event (for example if retrieving something from a server when there is an error). For async await you need to wrap potentially dangerous functions in a try catch.

In order to use await you need to be inside an async function (hence the async container function above). This is not necessary with .then, but .then and .catch chains can make your code messy.

I hope this helps!

JavaScript, async await is returning a promise instead of the result

resp.json() returns a Promise and that's what you are console logging. It should resolve before getting the actual data. Since you are in an async function, you could do as below. Notice there is no need to have this then block you have.

const GetProfile = async (username) => {
const res = await fetch(`${host}/api/v1/getprofile/${username}`);
const data = await res.json();
return data;
});
};

Async Function returns Promise pending

All async functions return a promise - always. Using await inside an async function suspends the execution of that function until the promise you are awaiting resolves or rejects, but an async function is not blocking to the outside world. An await does not suspend execution of the whole js interpreter.

At the point of the first await in the async function your function returns a promise back to the caller and the caller continues to execute. That promise is then resolved or rejects when the function eventually completes its work sometime in the future.

I don't understand why i get Promise { } since i used async/await

Because all async functions return a promise.


There are numerous issues in your code:

  1. Your getJSON() function has no return value. That means that the promise it returns resolves to undefined so there's not way to communicate back its value.
  2. await response.json needs to be await response.json().
  3. pokemonID should be passed as an argument, not just stuffed into a higher scoped variable.
  4. When calling getJSON(), you have to use .then() or await on it to get the resolved value from the returned promise.

You may also notice that your getJSON() function has no return value. That means that the promise it returns also resolves to undefined. And, await response.json needs to be await response.json().

Your code needs to be more like this:

async function getJSON(id) {
const response = await fetch('https://pokeapi.co/api/v2/pokemon/'+ id);
const json = await response.json();
return json;
}

const pokemonID = getRandomPokemon();
getJSON(pokemonID).then(data => {
console.log(data);
}).catch(err => {
console.log(err);
});

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.



Related Topics



Leave a reply



Submit