Reputation: 12874
An extension of this question, the solution by @CertainPerformance is working fine.
const query = qs.stringify({ ...API_QUERY_PARAMS, q: this.state.searchString });
const url = `https://www.googleapis.com/youtube/v3/search?${query}`
const { data } = await axios.get(url);
await Promise.all(data.items.map(async (vid) => {
let id = vid.id.videoId; //Individual video ID
const individualQuery = qs.stringify({ ...INDIVIDUAL_API_QUERY_PARAMS, id });
const individualURL = `https://www.googleapis.com/youtube/v3/videos?${individualQuery}`;
const { data } = await axios.get(individualURL);
vid.statistics = data.items[0].statistics
}))
this.setState({ videos: data.items });
I was thinking it could be use forEach
instead of map
. However if I swap to forEach
it will do nothing and state.videos
will return nothing.
Checking from this article, specifically this statement
Well, the forEach() method doesn’t actually return anything (undefined). It simply calls a provided function on each element in your array. This callback is allowed to mutate the calling array.
And hence forEach theoretically should work too but why it's not the case? For example, the forEach
should behave like below which able to mutate the calling array
let items = [
{id: '123', title: 'John'},
{id: '123', title:'sammy'}
]
items.forEach(x=> {
x['statistics'] = { propA: 'A', propB: 'B'};
})
console.log(items);
Upvotes: 2
Views: 439
Reputation: 370679
You need map
because Promise.all
requires an argument of an array, generally with all or some of those elements in the array as Promise
s. As you see, forEach
does not return an array. In fact, it won't run at all once it reaches that point, an error will be thrown:
Promise.all(undefined).then(() => console.log('ok'))
(index):30 Uncaught (in promise) TypeError: Cannot read property 'Symbol(Symbol.iterator)' of undefined at Function.all () at window.onload ((index):30)
You might be missing that async
functions automatically return Promises
that resolve once the end of their (visual) block is reached. Translating the async
function into a standard function does exactly the same thing and explicitly returns a Promise
, the code would look like this:
await Promise.all(data.items.map((vid) => {
const id = vid.id.videoId;
const individualQuery = qs.stringify({ ...INDIVIDUAL_API_QUERY_PARAMS, id });
const individualURL = `https://www.googleapis.com/youtube/v3/videos?${individualQuery}`;
return axios.get(individualURL)
.then(({ data }) => {
vid.statistics = data.items[0].statistics
});
}))
You have to return
the created Promise so that Promise.all
knows that it need to wait for all Promise
s in the array to resolve first - this was somewhat masked behind the async
function of the original code.
Upvotes: 2