Как я могу получить массив URL-адресов с Promise.all?
Если у меня есть массив URL-адресов:
var urls = ['1.txt', '2.txt', '3.txt']; // these text files contain "one", "two", "three", respectively.
И я хочу построить объект, который выглядит так:
var text = ['one', 'two', 'three'];
Я пытался научиться делать это с fetch
что, конечно, возвращает Promise
s.
Некоторые вещи, которые я пробовал, не работают:
var promises = urls.map(url => fetch(url));
var texts = [];
Promise.all(promises)
.then(results => {
results.forEach(result => result.text()).then(t => texts.push(t))
})
Это выглядит неправильно, и в любом случае это не работает - я не получаю массив ['one', 'two', 'three'].
Использует Promise.all
правильный подход здесь?
8 ответов
Да, Promise.all
это правильный подход, но на самом деле он вам нужен дважды, если вы хотите сначала fetch
все URL, а затем получить все text
s от них (которые снова являются обещаниями для тела ответа). Так что вам нужно сделать
Promise.all(urls.map(fetch)).then(responses =>
Promise.all(responses.map(res => res.text())
).then(texts => {
…
})
Ваш текущий код не работает, потому что forEach
ничего не возвращает (ни массив, ни обещание).
Конечно, вы можете упростить это и начать с получения тела из каждого ответа сразу после выполнения соответствующего обещания извлечения:
Promise.all(urls.map(url =>
fetch(url).then(resp => resp.text())
)).then(texts => {
…
})
По какой-то причине ни один из примеров Берги не работал для меня. Это просто даст мне пустые результаты. После некоторой отладки кажется, что обещание вернется до завершения извлечения, отсюда и пустые результаты.
Однако у Бенджамина Грюнбаума ранее был здесь ответ, но он его удалил. Его метод сработал для меня, поэтому я просто скопирую и вставлю его здесь, в качестве альтернативы на случай, если кто-нибудь еще столкнется с какими-либо проблемами с первым решением здесь.
var promises = urls.map(url => fetch(url).then(y => y.text()));
Promise.all(promises).then(results => {
// do something with results.
});
Вы должны использовать map
вместо forEach
:
Promise.all(urls.map(url => fetch(url)))
.then(resp => Promise.all( resp.map(r => r.text()) ))
.then(result => {
// ...
});
Вот простой способ сделать это.
const requests = urls.map((url) => fetch(url));
const responses = await Promise.all(requests);
const promises = responses.map((response) => response.text());
return await Promise.all(promises);
Предлагаемый массив
urls = ['1.txt', '2.txt', '3.txt']
для меня не имеет особого смысла, поэтому вместо этого я буду использовать:
urls = ['https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3']
JSON двух URL:
{"userId":1,"id":2,"title":"quis ut nam facilis et officia qui",
"completed":false}
{"userId":1,"id":3,"title":"fugiat veniam minus","completed":false}
Цель состоит в том, чтобы получить массив объектов, каждый из которых содержит значение из соответствующего URL.
Чтобы сделать его немного более интересным, я предполагаю, что уже существует массив имен, с которым я хочу объединить массив результатов URL ( заголовков):
namesonly = ['two', 'three']
Желаемый результат - это массив объектов:
[{"name":"two","loremipsum":"quis ut nam facilis et officia qui"},
{"name":"three","loremipsum":"fugiat veniam minus"}]
где я изменил имя атрибута
title
к
loremipsum
.
Справка
Следующее также работает для меня.
Promise.all([
fetch(QUESTIONS_API_BASE_URL).then(res => res.json()),
fetch(SUBMISSIONS_API_BASE_URL).then(res => res.json())
])
.then(console.log)
// Here, you have an array called todos containing three URLs.
const todos = [
"https://jsonplaceholder.typicode.com/todos/1",
"https://jsonplaceholder.typicode.com/todos/2",
"https://jsonplaceholder.typicode.com/todos/3",
];
// asunchronous function to fetch todo of the respective url
const fetchTodo = async (url) => {
const res = await fetch(url);
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`);
}
return res.json();
};
// The todos.map((item) => fetchTodo(item)) part creates an array of promises by mapping each URL to the result of the fetchTodo function. Promise.all then waits for all these promises to settle (either resolve or reject).
Promise.all(todos.map((item) => fetchTodo(item)))
.then((res) => {
console.log(res);
})
.catch((err) => console.error(err));
// fetches JSON data from multiple URLs concurrently using Promise.all,
В случае, если вы используете axios. Мы можем добиться этого следующим образом:
const apiCall = (конечная точка: строка)=> axios.get(
${baseUrl}/${endpoint}
)
axios.all([apiCall('https://first-endpoint'),apiCall('https://second-endpoint')]).then(response => {
response.forEach(values => values)
}).catch(error => {})