Reputation: 5613
Here I have a function that takes an array of string that contains the user names of github accounts. And this function is going to return an array of user data after resolving. There should be one fetch request per user. and requests shouldn’t wait for each other. So that the data arrives as soon as possible. If there’s no such user, the function should return null
in the resulting array.
An example for the input would be ["iliakan", "remy", "no.such.users"]
, and the expected returned promise
after resolving would give us [null, Object, Object]
, Object
being the data that contained info about a user.
Here is my attempt to solve this question.
function getUsers(names) {
return new Promise(resolve => {
const array = [];
const url = "https://api.github.com/users/";
const requests = names.map(name => {
const endpoint = `${url}${name}`;
return fetch(endpoint);
});
Promise.all(requests).then(reponses => {
reponses.forEach(response => {
if (response.status === 200) {
response.json().then(data => {
array.push(data);
});
} else {
array.push(null);
}
});
resolve(array);
});
});
}
It does work, i.e. returning an array [null, Object, Object]
. And I thought it fulfilled the requirements I stated above. However, after looking at it closely, I felt like I couldn't fully make sense of it.
My question is, look at where we resolve this array, it resolved immediately after the forEach
loop. One thing I don't understand is, why does it contain all three items when some of the items are pushed into it asynchronously after the json()
is finished. what I mean is, in the case where response.status === 200
, the array is pushed with the data resolved from json()
, and I would assume this json()
operation should take some time. Since we didn't resolve the array after json()
operation is finished, how come we still ended up with all data resolved from json()
?
Promise.all(requests).then(reponses => {
reponses.forEach(response => {
if (response.status === 200) {
response.json().then(data => {
array.push(data); <--- this should take some time
});
} else {
array.push(null);
}
});
resolve(array); <--- resolve the array immediately after the `forEach` loop
});
});
It looks to me like the array we get should only have one null
in it since at the time it is revolved, the .json()
should not be finished
Upvotes: 0
Views: 68
Reputation: 31805
You're right, the result is pushed later into the array.
Try to execute this:
const test = await getUsers(['Guerric-P']);
console.log(test.length);
You'll notice it displays 0
. Before the result is pushed into the array, its length is 0. You probably think it works because you click on the array in the console, after the result has arrived.
You should do something like this:
function getUsers(names) {
const array = [];
const url = "https://api.github.com/users/";
const requests = names.map(name => {
const endpoint = `${url}${name}`;
return fetch(endpoint);
});
return Promise.all(requests).then(responses => Promise.all(responses.map(x => x.status === 200 ? x.json() : null)));
};
Upvotes: 2
Reputation: 74204
You should avoid using the Promise
constructor directly. Here, we don't need to use it at all.
const url = "https://api.github.com/users/";
const getUsers = names =>
Promise.all(names.map(name =>
fetch(url + name).then(response =>
response.status === 200 ? response.json() : null)));
getUsers(["iliakan", "remy", "no.such.users"]).then(console.log);
The Promise
constructor should only be used when you're creating new kinds of asynchronous tasks. In this case, you don't need to use the Promise
constructor because fetch
already returns a promise.
You also don't need to maintain an array and push to it because Promise.all
resolves to an array. Finally, you don't need to map over the result of Promise.all
. You can transform the promises returned by fetch
.
Upvotes: 1
Reputation: 101
The thing is that because json()
operation is really quick, especially if response data is small in size it just has the time to execute. Second of all as objects in JavaScript passed by reference and not by value and Array
is a object in JavaScript, independently of execution time it'll still push that data to the array even after it was resolved.
Upvotes: 0