How to Fetch an Array of Urls with Promise.All

How can I fetch an array of URLs with Promise.all?

Yes, Promise.all is the right approach, but you actually need it twice if you want to first fetch all urls and then get all texts from them (which again are promises for the body of the response). So you'd need to do

Promise.all(urls.map(u=>fetch(u))).then(responses =>
Promise.all(responses.map(res => res.text()))
).then(texts => {

})

Your current code is not working because forEach returns nothing (neither an array nor a promise).

Of course you can simplify that and start with getting the body from each response right after the respective fetch promise fulfilled:

Promise.all(urls.map(url =>
fetch(url).then(resp => resp.text())
)).then(texts => {

})

or the same thing with await:

const texts = await Promise.all(urls.map(async url => {
const resp = await fetch(url);
return resp.text();
}));

How can I fetch an array of URLs without Promise.all?

The following solution relies on the second Stack Snippet of
this helpful answer.
1

const namesonly = ['two', 'three'];
const urls = ['https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3'];

const titles = [];
let countDown = urls.length;
urls.forEach((url, index) => {
asynchronousCall(url, title => {
titles[index] = title;
if (--countDown === 0) { // Callback for ALL starts on next line.
const names = namesonly.map(value => ({ name: value }));
const fakeLatins = titles.map(value => ({ loremipsum: value }));
const result =
names.map((item, i) => Object.assign({}, item, fakeLatins[i]));
console.log('result:\n' + JSON.stringify(result));
}
});
});
function asynchronousCall (url, callback) {
console.log('Starting fetch for "' + url + '".');
fetch(url).then(response => response.json()).then(responseBody => {
console.log(url.slice(-1) + ': ' + responseBody.title + ' ...');
console.log('... fetch for ' + url + ' completed!');
callback(responseBody.title); // Individual callback.
});
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

Using Promise.all() to fetch a list of urls with await statements

You have several problems with your code on a fundamental basis. We should address those in order and the first is that you're not passing in any URLS!

async function fetchAll(urls) {
let data
let ext
try {
data = await Promise.all(urls.map(url=>fetch(url)))
} catch (err) {
console.log(err)
}
try {
ext = await Promise.all(data.map(res => {
if (res.json()==! 'undefined') { return res.json()}
}))
} catch (err) {
console.log(err)
}
for (let item of ext) {
console.log(ext)
}
}

First you have several try catch blocks on DEPENDANT DATA. They should all be in a single try catch block:

async function fetchAll(urls) {
try {
let data = await Promise.all(urls.map(url=>fetch(url)))
let ext = await Promise.all(data.map(res => {
// also fixed the ==! 'undefined'
if (res.json() !== undefined) { return res.json()}
}))
for (let item of ext) {
console.log(ext)
}
} catch (err) {
console.log(err)
}
}

Next is the problem that res.json() returns a promise wrapped around an object if it exists

if (res.json() !== undefined) { return res.json()}

This is not how you should be using the .json() method. It will fail if there is no parsable json. You should be putting a .catch on it

async function fetchAll(urls) {
try {
let data = await Promise.all(urls.map(url => fetch(url).catch(err => err)))
let ext = await Promise.all(data.map(res => res.json ? res.json().catch(err => err) : res))
for (let item of ext) {
console.log(ext)
}
} catch (err) {
console.log(err)
}
}

Now when it cannot fetch a URL, or parse a JSON you'll get the error and it will cascade down without throwing. Now your try catch block will ONLY throw if there is a different error that happens.

Of course this means we're putting an error handler on each promise and cascading the error, but that's not exactly a bad thing as it allows ALL of the fetches to happen and for you to distinguish which fetches failed. Which is a lot better than just having a generic handler for all fetches and not knowing which one failed.

But now we have it in a form where we can see that there is some better optimizations that can be performed to the code

async function fetchAll(urls) {
try {
let ext = await Promise.all(
urls.map(url => fetch(url)
.then(r => r.json())
.catch(error => ({ error, url }))
)
)
for (let item of ext) {
console.log(ext)
}
} catch (err) {
console.log(err)
}
}

Now with a much smaller footprint, better error handling, and readable, maintainable code, we can decide what we eventually want to return. Now the function can live wherever, be reused, and all it takes is a single array of simple GET URLs.

Next step is to do something with them so we probably want to return the array, which will be wrapped in a promise, and realistically we want the error to bubble since we've handled each fetch error, so we should also remove the try catch. At that point making it async no longer helps, and actively harms. Eventually we get a small function that groups all URL resolutions, or errors with their respective URL that we can easily filter over, map over, and chain!

function fetchAll(urls) {
return Promise.all(
urls.map(url => fetch(url)
.then(r => r.json())
.then(data => ({ data, url }))
.catch(error => ({ error, url }))
)
)
}

Now we get back an array of similar objects, each with the url it fetched, and either data or an error field! This makes chaining and inspecting SUPER easy.

Using Promise.all to resolve fetch requests

I can't see any problems, for me it returns just fine: https://jsfiddle.net/np5bx03j/
However, this is a test with jsfiddles /echo/json URLs and not your original ones. I therefore would assume some error occured in your case.
I suggest adding a catch to log errors:

Promise.all(requestsArray.map((request) => {
return fetch(request).then((response) => {
return response.json();
}).then((data) => {
return data;
});
})).then((values) => {
console.log('values', values);
}).catch(console.error.bind(console));

EDIT: Just for the sake of completeness: I can't see any problems according to the API (MDN) or anything else either.

Promise.all fetch continue executing after throwing error?

I would recommend encapsulating the success / failure logic for individual requests, then you can determine all the resolved and rejected responses based on the result of that encapsulation.

For example

const checkKeyword = async (ref, keyword) => {
const params = new URLSearchParams({ search: keyword });
const res = await fetch(
`https://developer.wordpress.org/wp-json/wp/v2/wp-parser-${encodeURIComponent(
ref
)}?${params}`
);

if (!res.ok) {
throw new Error(`${res.status}: ${await res.text()}`);
}

const data = await res.json();

if (data.length === 0) {
throw new Error(`Empty results for '${ref}'`);
}

return { ref, data };
};

Now you can use something like Promise.any() or Promise.allSettled() to find the first successful request or all successful requests, respectively

const keyword = "AtomParser";
const refs = ["function", "hook", "class", "method"];

const promises = refs.map((ref) => checkKeyword(ref, keyword));

// First success
Promise.any(promises)
.then(({ ref, data }) => {
console.log(ref, data);
})
.catch(console.error);

// All successes
Promise.allSettled(promises)
.then((responses) =>
responses.reduce(
(arr, { status, value }) =>
status === "fulfilled" ? [...arr, value] : arr,
[]
)
)
.then((results) => {
// results has all the successful responses
});

How to return the Promise.all fetch api json data?

Aparently aa.json() and bb.json() are returned before being resolved, adding async/await to that will solve the problem :

.then(async([aa, bb]) => {
const a = await aa.json();
const b = await bb.json();
return [a, b]
})

Promise.all([    fetch('https://jsonplaceholder.typicode.com/todos/1'),    fetch('https://jsonplaceholder.typicode.com/todos/2')  ]).then(async([aa, bb]) => {    const a = await aa.json();    const b = await bb.json();    return [a, b]  })  .then((responseText) => {    console.log(responseText);
}).catch((err) => { console.log(err); });


Related Topics



Leave a reply



Submit