I can't seem to figure out how to set the value of an array inside a loop...
As you can see below, I retrieve some values from the database and loop through those.
What I try to do here can be sum up approximately to the following:
- request_1(pk)
- getting all the bookings ids from that client then
- I get the list of bookings then
- loop through all bookings for getting the details which include the id of the product
- request_2
- looping again through the ids and getting the details of each product
- request_3
Then I try to store those values that I get but it does not work.]
When I display what is pushed to the array inside the loops, it seems to work (ie I get the values meant to be retrieved). Yet, when outside the loops, the array is completely empty...
Here the code, so far:
const request = async (pk) => {
try {
let singleDetail = [];
const firstResponse = await axios.get(
`http://localhost:8000/${pk}`
);
firstResponse.data.map(async (book, idx) => {
const booking = JSON.stringify(book.id);
const secondResponse = await axios.get(
`http://localhost:8000/${booking}`
);
Object.keys(secondResponse.data).map(async (single) => {
let course = JSON.stringify(secondResponse.data[single].courses);
const thirdResponse = await axios.get(
`http://localhost:8000/${course}`
);
singleDetail.push(
JSON.stringify({
booking: booking,
single: JSON.stringify(secondResponse.data[single]),
course: JSON.stringify(thirdResponse.data),
})
);
});
});
console.log("SINGLEDETAILS " JSON.stringify(singleDetail));
return singleDetail;
} catch (error) {
console.log(error);
}
};
CodePudding user response:
From the example, the issue looks to be that you are not awaiting the async functions from the maps.
Without awaiting the results of firstResponse.data.map(async (book, idx) => {...})
, JS will skip waiting for the promises to resolve and thus jump straight to return singleDetail;
By using await Promise.all(...)
, you tell JS to wait for all the promises in the array to succeed before continuing.
Try:
const request = async (pk) => {
try {
let singleDetail = [];
const firstResponse = await axios.get(
`http://localhost:8000/${pk}`
);
await Promise.all(firstResponse.data.map(async (book, idx) => {
const booking = JSON.stringify(book.id);
const secondResponse = await axios.get(
`http://localhost:8000/${booking}`
);
await Promise.all(Object.keys(secondResponse.data).map(async (single) => {
let course = JSON.stringify(secondResponse.data[single].courses);
const thirdResponse = await axios.get(
`http://localhost:8000/${course}`
);
singleDetail.push(
JSON.stringify({
booking: booking,
single: JSON.stringify(secondResponse.data[single]),
course: JSON.stringify(thirdResponse.data),
})
);
}));
}));
console.log("SINGLEDETAILS " JSON.stringify(singleDetail));
return singleDetail;
} catch (error) {
console.log(error);
}
};
If a single API request fails, the try/catch will catch the error and none of the successful data requests will be of any use.
If it is desired to use whatever data you can capture (while other requests in the loop fail), you could modify the code to catch errors after each request instead.
If there are a lot of request happening, you may wish to use something like https://www.npmjs.com/package/p-map instead of await Promise.all(...)
to throttle the number of outbound connections.