Why doesn't this async function throw undefined? - javascript

I have a pretty simple async function that calls the fetch api, and brings me back some data. I am using the await keyword 2 times in this function, and then getting that data and pushing it into my component state.
Here is my pseudo-code in regards to how this function is executing (please tell me if I'm right or wrong here):
Call the fetch api with await: this allows the rest of your code to continue to the next line.
Once you get the fetch the response stream, put it into data variable. Again, the code can continue while we are waiting for this to happen.
Log the data to the console.
Step 3 is where I have some questions...let's say I'm on a really terrible network, and my fetch request doesn't give me my data for 5 full seconds. At that point, shouldn't my console.log(data) line throw undefined, and execute the catch block, due to the async function allowing console.log(data to run BEFORE I get my fetch data back?
I tested this by going into the Chrome Web dev console, and selected the "slow 3g" connection. Even with that network, I was able to log my data to the console without throwing undefined.
What I want to do is to make sure there is data to push into state right after I get my data object back.
getMovieDetails = async id => {
const API_KEY = process.env.REACT_APP_API_KEY;
const movieId = id;
const url = `https://api.themoviedb.org/3/movie/${movieId}?api_key=${API_KEY}&language=en-US`;
try {
const response = await fetch(url);
const data = await response.json();
console.log(data);
this.setState({
title: data.title,
poster: data.poster_path
});
} catch (err) {
console.log(`Couldn't fetch the endpoint!`);
console.log(err);
}
};

You are wrong in the first point of your Pseudo-code
Call the fetch api with await: this allows the rest of your code to
continue to the next line.
Actually, no. await will block the execution of the next lines in the async function.
So,
yourAsyncFunction = async () => {
await doSomething();
console.log('done something') // Will not run until doSomething() gets completed
}
Which is why you are always getting the fetched data in your console.log(data) statement.

Related

async or promise all, reject all if first fails - 3 post method calls

Edited. I need to call addUser() to receive the userId back thats generated from the database. Then i need to call addTicket() with the userId to receive the ticketId back thats generated from the database. Then finally call addToBooks with the userId and ticketId. These are all post methods, the only data I need back from each call are their ids, the last post method will return full data. Is there a way to handle this where I can fail all post methods if any one of the fails in the process? I've seen examples of promise.all that can do this but it was all for get methods.
You could try something like this, following your functions names example:
const getData = async () => {
try {
const userId = await addUser();
if(userId) {
const ticketId = await addTicket(userId);
const response = await addToBooks(userId, ticketId);
return response;
} else {
throw new Error("Error getting data")
}
} catch(error) {
console.log(error);
}
}
This doesn't use Promise.all, but since the example you are giving, to my understanding, only needs the userId to complete the other 2 operations, I think this should work

Aborting nested fetch requests when component is unmounting

The following function, when consumed, fetches and returns an array of up to 50 comments of a single post. It first fetches a single post by ID, and this post object has an array of comments ID's, which it will fetch.
My goal is to early abort this task in my React class component using componentWillUnmount, by calling abort on the signal on the class instance. The signal is passed as abortSignal.
The question is, I have a case of nested fetch requests. How should I approach this so I can make sure any on-going fetch requests are aborted when the component is unmounting? As I seee it, passing the signal to the outer fetch won't suffice if this stage was already completed. Should I create another signal inside of this function, and pass to to individual fetch?
const fetchComments = async (type, abortSignal) => {
const res = await fetch(endpoints[`${type}Stories`]);
const post = await res.json();
return Promise.all(post.slice(0, 50).map(async id => {
const url = endpoints.singleStory.replace('[id]', id);
const comment = await fetch(url);
return comment.json();
}));
}
I don't see any reason you can't reuse the abortSignal across all the fetch calls. You probably also want to check its aborted flag after awaiting so you bail proactively:
const fetchComments = async (type, signal) => {
const res = await fetch(endpoints[`${type}Stories`], {signal});
if (signal.aborted) {
return; // Or whatever
}
if (!res.ok) {
throw new Error("HTTP error " + res.status);
}
const post = await res.json();
if (signal.aborted) {
return; // Or whatever
}
return Promise.all(post.slice(0, 50).map(async id => {
// Probably not much point to checking `signal.aborted` here, you *just* checked it above
const url = endpoints.singleStory.replace('[id]', id);
const comment = await fetch(url, {signal});
if (!comment.ok) {
throw new Error("HTTP error " + comment.status);
}
return comment.json();
}));
}
Two notes on that:
I changed abortSignal to signal so I could use shorthand property notation when passing it to fetch, because I'm lazy. :-)
Your code is falling prey to the fetch footgun — you need to check for HTTP success, it only rejects on network error, not HTTP error. I've inserted checks above.
Note: If you can modify the API, I'd strongly recommend making it possible to ask for the 50 comments as part of the initial fetch, or at least to be able to ask for a batch of comments, rather than loading each comment individually with its own HTTP request (although HTTP/2 helps a lot).

How to do asynchronous calls in vanilla javascript

I managed to use axios by putting this link in my html:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
What I am trying to do is call axios twice, once to call my http request to send in a JSON Object, and another to send in a file along with the JSON object. What I want to do is to make it so that it is required for both to be sent, meaning that the user cannot just send the JSON object by itself or the file by itself. However, my code does not check for that for some reason. I believe it is due to async not being present, But idk how to add that when I am using vanilla js. Here is my code:
axios.post('/account/signup', userObj).then((profile)=>{
//data returns current user info, including if they are verified or not
return axios.post('/account/signup/veteranFile-upload', formData)
}).then(()=>{
//window.location.replace('/')
console.log('Hello?')
}).catch( function (error){
showAppropriateTextSignUp(error.response.data, signupErr)
})
For some extra info, my model requires the file and the JSON data being sent.
your second then is in the wrong place. also is formData defined?
axios.post('/account/signup', userObj).then((profile)=>{
//data returns current user info, including if they are verified or not
return axios.post('/account/signup/veteranFile-upload', formData).then(()=>{
//window.location.replace('/')
console.log('Hello?')
}).catch( function (error){
showAppropriateTextSignUp(error.response.data, signupErr)
});
});
a much cleaner way: use async await:
const api = async() => {
try{
const profile = await axios.post('/account/signup', userObj);
const whatever = axios.post('/account/signup/veteranFile-upload', formData);
console.log(whatever)
} catch (e) {
showAppropriateTextSignUp(error.response.data, signupErr)
}
}
you might need to chain the axios call instead of returning. That is
axios.post('/account/signup', userObj)
.then((profile)=>{
//current user info, including if they are verified or not
axios.post('/account/signup/veteranFile-upload', formData).then(resp=>{
//do something with the response
//window.location.replace('/')
}).catch(err=>{
console.log(err)
})
console.log('Hello?')
}).catch( function (error){
showAppropriateTextSignUp(error.response.data, signupErr)
})

async / await handling error in node js mongodb

I'm using async function in my node, here I'm calling multiple queries with mongodb
(i.e) :
let data1;
let data2;
try{
data1 = await mongoQuery1
}catch {
res.send(error)
}
try{
data2 = await mongoQuery2
}catch {
res.send(error)
}
res.send({ data1, data2 });
I've confused with this flow. So, basically All I want to send to user is both data1 and data2 but what happens if the query1 fails because, it will be obviously falls in the first catch block and send response to the user, and then continue executing the next try block, and send the response again, which is totally wrong. How can I achieve this, what I want is if any error occured in any catch I want to send the error response back to the user
If all the try block succeeds then only I want to send back the success response to the user, How can I achieve this?
It's not really clear what you are trying to do.
If you want to stop and just send the error then you can return after calling res.send to stop the function continuing.
If you want to send the error with the rest of the data then don't call res.send immediately, store the error in data1 and then send it at the end as normal.
Advice to use Promise.all for your scenario:
Both query will be run simultaneously and if there is an error it will immediately throw and got to the catch block.
try {
const [data1, data2] = await Promise.all([mongoQuery1, mongoQuery2]);
res.send({ data1, data2 })
} catch {
res.send(error)
}

Data part of Response is a long script instead of desired json object

I am building a web app using laravel and vuejs. I have made a axios get request to get a list of users .
I am getting a Promise object, and from what i have read. Reason for getting a promise object is because it's an async request.
I have tried .then() to get data part of the response. But i am getting a huge script instead of desired data.
axios......then(function(response){
console.log(response.data);
})
Initially what i did was
var res = axios.get('/allUsers');
console.log(res)
That time i came to know about promise object and read about.
When i checked network in dev tools, status code is 200 and i can see list of users. So i guess my request is successfully completed.
What should be done to get the list of the users. That list i will be using to update my UI.
Depending on what you're getting back for data there are a few ways to handle this. You may need to convert the data after the you get receive the response.
axios.get('some_url')
.then(res => res.json())
.then(data => {
// do something with the data
}).catch(err) {
conosole.error(err);
}
if you're seeing the data come through properly in the response and you're getting what you need without doing that then just do
axios.get('some url').then(res => {
// do something in here with the data here
})
also make sure you're getting back json if that's what you're looking for. check your response to see if its html or json because they can be handled a bit differently
as an "Edit" you could also handle this with async await so you dont end up in callback hell
async function fetchData() {
try {
const res = await axios.get('some url');
// next step might not be necessary
const data = await res.json();
// do something with the data
console.log(data); // if converting it was necessary
console.log(res); // if not converting
} catch (err) {
console.error(err);
}
}

Categories

Resources