When using await does it block the function it is used in? - javascript

I have this async function below:
async LoadEditForm() {
const { default : EditHero } = await import('./EditHero');
this.setState({ lazyEditHero: EditHero })
}
And it is called it here:
handleEnableAddMode = async () => {
await this.LoadEditForm();
this.setState({
addingHero: true,
selectedHero: { id: '', name: '', saying: '' }
});
}
I have a question regarding the code above:
At this line: await this.LoadEditForm(); in the handleEnableAddMode function, does it block that function? In other words does the this.setState(...) function get called immediately or does it wait for the await call to finish.
Since I heard people saying the async/await concept is to allow you to write async code in a synchronous manner. That is kind of confusing me. Can someone clarify?

At this line: await this.LoadEditForm(); in the handleEnableAddMode function, does it block that function? In other words does the this.setState(...) function get called immediately or does it wait for the await call to finish.
One of those is not the other one "in other words".
It doesn't block, it waits. The control will return to the caller of the async function. When the promise being waited on is resolved or rejected, then the waiting function will resume.
https://jsfiddle.net/7yxhqoo4/ has a simple example, thus:
function resolveAfterHalfSecond() {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, 500);
});
}
async function f1() {
$("div").append("<p>before await</p>");
await resolveAfterHalfSecond();
$("div").append("<p>after await</p>");
}
function test()
{
$("div").append("<p>before call async function</p>");
f1();
$("div").append("<p>after call async function</p>");
}
$(test);
The immediate output looks like:
before call async function
before await
after call async function
And after half a second, it looks like
before call async function
before await
after call async function
after await.
When the await was hit in f1() it didn't block, but immediately went back to test() and finished that function. When the promise was resolved, f1() resumed.

Typically await will hold until it receives either a successful result because the promise was "resolved", or it encounters an error because the promise was "rejected".
If neither of these two events occur, your promise is broken and the await may never complete.

When you're calling an Asyc function, it will return a promise, whenever you return a value in your async function your promise will be resolved, and if you throw an exception, your promise is rejected. On the other side, the await expression pauses the execution of the async function and waits for the passed Promise's resolution, and then resumes the async function's execution and returns the resolved value.
Based on the provided samples in Mozile documentations:
function getProcessedData(url) {
return downloadData(url) // returns a promise
.catch(e => {
return downloadFallbackData(url) // returns a promise
})
.then(v => {
return processDataInWorker(v); // returns a promise
});
}
async function getProcessedData(url) {
let v;
try {
v = await downloadData(url);
} catch(e) {
v = await downloadFallbackData(url);
}
return processDataInWorker(v);
}
In your case, your setState won't be executed until your await this.LoadEditForm(); be fulfilled. But the thing is you've not catch the probable rejection of your promise. So if the function b rejected, your setState will be executed anyway.
So you have to wrap the await function and the following lines in a try ... catch statement.

Related

async function returning a promise and not a value [duplicate]

I'm trying async/await functionality. I have such code imitating a request:
const getJSON = async () => {
const request = () => new Promise((resolve, reject) => (
setTimeout(() => resolve({ foo: 'bar'}), 2000)
));
const json = await request();
return json;
}
When I use the code in this way
console.log(getJSON()); // returns Promise
it returns a Promise
but when I call this line of code
getJSON().then(json => console.log(json)); // prints { foo: 'bar' }
it prints json as expected
Is it possible to use just code like console.log(getJSON())? What don't I understand?
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.
A function defined with async always returns a Promise. If you return any other value that is not a Promise, it will be implicitly wrapped in a Promise. The statement const json = await request(); unwraps the Promise returned by request() to a plain object { foo: 'bar' }. This is then wrapped in a Promise before being returned from getJSON so a Promise is what you ultimately get when you call getJSON(). So to unwrap it, you can either call getJSON().then() like you've done or do await getJSON() to get the resolved value.
Return value of an async function will always be an AsyncFunction Object, which will return a Promise when called. You can not change that return type. The point of async/await is to easily wait for other async process to complete inside an async function.
const getResOrErr = () => {
const callAsyncCodeHere = async () => {
const request = () =>
new Promise((resolve, reject) =>
setTimeout(() => resolve({ foo: "bar" }), 2000)
);
const json = await request();
return json;
};
return callAsyncCodeHere()
.then(console.log)
.catch(console.log);
};
getResOrErr();
Try this. You can achieve making a function inside your main function and then put you promise code inside that function. Call it there and when you get the response or error just return it.

Handling Promise rejection with catch while using await

I am using await to make the code cleaner, but I am not sure whether I am handling exceptions correctly.
An example while using azure-devops-node-api;
const foo = async() => {
return new Promise((resolve, reject) => {
...
...
const teams = await coreApiObject.getTeams(currProject.id)
.catch(err => { reject(err) return })
...
...
})
}
In this code I am assuming, if there is a problem with promise call, foo() is going to return reject.
async functions always return a promise, so you don't need to explicitly create one yourself. Any non-promise value returned from an async function is implicitly wrapped in a promise.
Inside the foo function, you just need to await the call coreApiObject.getTeams(...) and to catch and handle any error, use the try-catch block.
Your code can be simplified as shown below:
const foo = async() => {
try {
const teams = await coreApiObject.getTeams(currProject.id);
return teams;
} catch (e) {
// handle error
}
}
If you want to the calling code to handle the error, then you can use one of the following options:
Remove the try-catch block and just return the result of coreApiObject.getTeams(...).
const foo = async() => {
return coreApiObject.getTeams(currProject.id);
}
Removing the try-catch block and just returning the call to coreApiObject.getTeams(...) will allow the calling code to handle the error because the promise returned by the foo function will get resolved to the promise returned by coreApiObject.getTeams(...); this means that the fate of the promise returned by the foo function will depend on whatever happens to the promise returned by coreApiObject.getTeams(...).
If the promise returned by coreApiObject.getTeams(...) is rejected, promise returned by the foo function will also be rejected and hence the calling code will have a change to catch the promise rejection and handle it.
Throw the error from the catch block.
const foo = async() => {
try {
const teams = await coreApiObject.getTeams(currProject.id);
return teams;
} catch (error) {
// throw the error
throw error;
}
}
Other option is to throw the error from the catch block to make sure that the promise returned by the async function is rejected.
If you don't throw the error or return a promise or a thenable that is rejected, returning any other value from the catch block will fulfil the promise returned by the async function with whatever value is returned inside the catch block.

Why does my async function always return a pending promise? [duplicate]

This question already has answers here:
async/await implicitly returns promise?
(5 answers)
Closed 1 year ago.
When I console.log(data), I log the information I need, but if return the value of data in getWeather(), it just returns a pending promise. I have tried many things, but none have worked so far. I'll leave my broken code below.
const axios = require('axios');
const getWeather = async () => {
try {
let response = await axios.get(
'http://api.openweathermap.org/data/2.5/forecast?id=524901&appid={apiKey}'
);
let data = response.data;
return data;
} catch (e) {
console.log(e);
}
};
async function returnAsync() {
const x = await getWeather();
return x;
}
console.log(getWeather()); // returns a pending promise
console.log('check ', returnAsync()); // also returns a pending promise
async functions must return a promise. (they implicitly return Promise<void> instead of void!)
Async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.
For example, the following:
async function foo() {
return 1
}
...is equivalent to:
function foo() {
return Promise.resolve(1)
}
Source
This could be to do with the fact that you're trying to call an async function inside of a synchronous function. It's quite a common mistake to make, so don't fret. Typically if you think about the structure, the console logs could technically be run before the async function has completed. That's why we have "callback functions" which basically just run as soon as the async returns a value.
Axios has a really neat way to use these callbacks which is just using the .then() function.
So try this out and see if it works:
const axios = require('axios');
const getWeather = async () => {
axios.get('http://api.openweathermap.org/data/2.5/forecast?id=524901&appid={apiKey}')
.then(json => console.log(json))
.catch(error => console.log(error))
};
getWeather();
Because you're trying to call the get request inside of an async function, you cannot then grab the data in a synchronous function.
//const getWeather = async () => {...
An async function's return value will be a Promise which will be resolved with the value returned by the async function, or rejected with an exception thrown from, or uncaught within, the async function.
check more at
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

How can I order a series of api calls from the frontend?

I have a series of API calls I need to make from a 'user profile' page on my app. I need to prioritize or order the calls when I land on the component.
I have tried using async-await on the componentDidMount lifecycle method but when the first call fails, the rest do not get called.
...
async componentDidMount() {
await user.getGameStats();
await user.getFriendsList();
await user.getPlayStyle();
}
...
Despite ordering the calls, I would like them to still execute regardless of whether the preceding calls failed.
You need to account for rejected promises. If you don't catch the error it will stop execution. Just add a catch() block to each function that may fail.
function a(){
return new Promise((r,f)=>{
console.log('a');
r();
});
}
function b(){
return new Promise((r,f)=>{
console.log('b');
f(); // rejecting this promise
});
}
function c(){
return new Promise((r,f)=>{
console.log('c');
r();
});
}
async function d(){
throw new Error('Something broke');
}
(async ()=>{
await a().catch(()=>console.log('caught a'));
await b().catch(()=>console.log('caught b'));
await c().catch(()=>console.log('caught c'));
await d().catch(()=>console.log('caught d'));
})();
It's a dirty solution, but you can do something like this:
user.getGameStats().then({res => {
callGetFriendsList();
}).catch({err =>
callGetFriendsList();
});
function callGetFriendsList() {
user.getFriendsList().then(res => {
user.getPlayStyle();
}).catch(err => {
user.getPlayStyle();
});
}
The ideal and good way would be to call all of them at the same time asynchronously if they are not dependent on the response of the previous request.
Just add an empty catch at the end of each API call as following
async componentDidMount() {
await user.getGameStats().catch(err=>{});
await user.getFriendsList().catch(err=>{});
await user.getPlayStyle().catch(err=>{});
}
I'd catch to null:
const nullOnErr = promise => promise.catch(() => null);
async componentDidMount() {
const [gameStats, friendsList, playStyle] = await Promise.all([
nullOnErr(user.getGameStats()),
nullOnErr(user.getFriendsList()),
nullOnErr(user.getPlayStyle())
]);
//...
}
I also used Promise.all to run the calls in parallel as there seems to be no dependency between the calls.

How to have one function wait for another function to finish processing in javascript using chaining?

I have a function that pulls an array from firebase via its rest api, which I need to input into another function to create a calendar.
function array_from_firebase(){
//code that pulls from firebase
return array
}
function calendar_create_from_array(array){
//code that generates calendar
}
The following does not work:
calendar_create_from_array(array_from_firebase())
However, this does work
array = array_from_firebase()
setTimeout(calendar_create_from_array,9000,array)
I believe this means that array_from_firebase takes a bit of time longer than calendar_create_from_array and calendar_create_from_array triggers too quickly.
How could I use chaining and promises to solve this?
From the first function you can return a promise. In this example usage of setTimeout is just to demonstrate that the next function execute after the first function
function array_from_firebase() {
return new Promise(
function(resolve) {
setTimeout(function() {
return resolve([1, 2, 3])
}, 4000)
},
function(reject) {})
}
function calendar_create_from_array(array) {
console.log(array);
}
array_from_firebase().then(function(resp) {
calendar_create_from_array(resp)
})
You can also use async & await.In this case the function which will wait for the result from another function need to be declared as async and declare await keyword before the function which will return the result.
function array_from_firebase() {
return new Promise(
function(resolve) {
setTimeout(function() {
return resolve([1, 2, 3])
}, 5000)
},
function(reject) {})
}
async function calendar_create_from_array() {
console.log('Calling function and waiting for result for 5secs....')
let getResult = await array_from_firebase();
console.log('Got result after 5secs', getResult)
}
calendar_create_from_array()
When you work with asynchronous code, you usually deal with Promises, which you have handle with .then and .catch blocks, to force you functions to wait for response. I've simulated the async call inside array_from_firebase function, it returns the response after 3 seconds, and the response is a Promise. Then you can handle this promise via .then block (as shown in calendar_create_from_array function), it will be executed, when you get the response with no errors, and .catch block will be executed when you get the error in response. Something like this:
function array_from_firebase() {
// Simulate async response
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve([1,2,3]);
}, 3000);
})
}
function calendar_create_from_array() {
array_from_firebase()
.then(function(response) {
console.log(response);
})
}
calendar_create_from_array();

Categories