Javascript 如何链接多个fetch()承诺?
下面的代码获取一个json列表,然后对每个列表项执行另一个获取调用以更改其值。问题是它不是同步完成的。在“更新”之前,“新建”将打印到控制台Javascript 如何链接多个fetch()承诺?,javascript,fetch-api,Javascript,Fetch Api,下面的代码获取一个json列表,然后对每个列表项执行另一个获取调用以更改其值。问题是它不是同步完成的。在“更新”之前,“新建”将打印到控制台 fetch(API_URL_DIARY) .then(response => response.json()) .then(data => { console.log("old", data); return data; }) .then(data => { data.forEach(function(e, index,arr
fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
console.log("old", data);
return data;
})
.then(data => {
data.forEach(function(e, index,array) {
fetch(API_URL_FOOD_DETAILS + e.foodid)
.then(response => response.json())
.then(data => {
array[index] = {...e, ...data};
console.log("update");
})
});
console.log("new", data)
});
更新
以下是我如何合并@Andy的解决方案:
function fetchFoodDetails(id, index) {
return fetch(API_URL_FOOD_DETAILS + id)
.then(response => response.json())
.then(data => {
return [index, data];
});
}
function fetchDiary() {
return fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
return data;
})
}
(async () => {
const data = await fetchDiary();
console.log("old", JSON.stringify(data));
const promises = data.map((food, index) => fetchFoodDetails(food.id, index));
await Promise.all(promises).then(responses => {
responses.map(response => {
data[response[0]] = {...data[response[0]], ...response[1]};
console.log("update");
})
});
console.log('new', JSON.stringify(data));
})();
这更难,所以我采用了@connoraworden的解决方案。但我认为它可以简化
谢谢你的回答
如何链接多个fetch()承诺
您可以像以前那样做,只需附加另一个。然后()
如果只想显示一次“console.log(“new”,data)”,可以使用索引进行检查,如下所示:
fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
console.log("old", data);
return data;
})
.then(data => {
data.forEach(function(e, index,array) {
fetch(API_URL_FOOD_DETAILS + e.foodid)
.then(response => response.json())
.then(data => {
array[index] = {...e, ...data};
console.log("update");
if ((data.length - 1) === index) { // CHECK INDEX HERE IF IS THE LAST
console.log("new", data)
}
})
});
});
您需要一个递归函数来完成此操作
fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
console.log("old", data);
return data;
})
.then(data => {
recursiveFetch(data)
});
function recursiveFetch(initialData){
e = initialData[initialData.length-1]; //taking the last item in array
fetch(API_URL_FOOD_DETAILS + e.foodid)
.then(response => response.json())
.then(data => {
array[index] = {...e, ...data};
console.log("update");
initialData.pop() // removing last item from array, which is already processed
if(initialData.length > 0)
recursiveFetch(initialData)
})
}
注意:这是一个未经测试的代码。fetch
是一个承诺。这是异步调用,因此“new”console.log在完成所有承诺之前运行。为此,请使用Promise.all()
您可以这样做:
fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
console.log("old", data);
return data;
})
.then(data => {
return Promise.all(data.map(food =>
fetch(API_URL_FOOD_DETAILS + food.foodid)
.then(resp => resp.json())
.then(json => {
// do some work with json
return json
})
))
})
.then(data => console.log('new', data))
您不应该在此处使用forEach
。最好的解决方案是使用等待一系列承诺(fetch
是一个承诺)来解析所有承诺,然后可以处理数据
在这里,我用一些示例数据创建了一个虚拟的fetch函数,以快速向您展示它是如何工作的
const dummoyobj={
main:[{id:1},{id:2},{id:5}],
其他:{
1:‘数据1’,
2:‘数据2’,
3:‘数据3’,
4:‘数据4’,
5:‘数据5’,
6:‘数据6’,
7:‘数据7’,
}
}
//summy函数只返回样本的一个子集
//2秒后的数据取决于类型和id参数
//模拟API调用
函数dummyFetch(类型,id){
返回新承诺((解决)=>{
设置超时(()=>{
解析(id?dummoybj[type][id]:dummoybj[type]);
}, 2000);
});
}
//在第一次获取中,我们只显示数据
//就像你在例子中做的那样
dummyFetch('main'))
。然后(数据=>{
console.log(“旧”,数据);
返回数据;
})
。然后(数据=>{
//使用Array.map而不是forEach来迭代
//并为每个
const promises=data.map(o=>dummyFetch('other',o.id));
//然后,您可以等待所有承诺得到解决
承诺。所有(承诺)。然后((数据)=>{
//在这里,您将迭代返回的组数据
//(如您的示例所示)
//我只是将新数据作为字符串记录
log(JSON.stringify(data));
//最后,最后是新的日志
console.log(“新”,数据)
});
});代码>最好的方法是使用Promise.all()
和map()
在此上下文中,映射将执行什么操作?返回fetch
中的所有承诺
然后将发生的是wait
将使您的代码执行同步,因为它将在继续执行之前等待所有承诺得到解决
在这里使用forEach
的问题在于,它不会等到异步请求完成后再移动到下一项
您应该在此处使用的代码是:
fetch(API_URL_DIARY)
.then(response => response.json())
.then(data => {
console.log("old", data);
return data;
})
.then(async data => {
await Promise.all(data.map((e, index, array) => {
return fetch(API_URL_FOOD_DETAILS + e.foodid)
.then(response => response.json())
.then(data => {
array[index] = {...e, ...data};
console.log("update");
})
}));
console.log("new", data)
});
在单个数组中存储多个响应
下面的代码在查询中获取多个关键字,并将所有三个响应的所有响应存储到all数组中
let queries = ["food", "movies", "news"]
let all = []
queries.forEach((keyword)=>{
let [subres] = await Promise.all([fetch(`https://reddit.com/r/${keyword}/hot.json?limit=100`).then((response) => response.json())]);
all.push(subres)
})
//now you can use the data globally or use the data to fetch more data
console.log(all)
您希望在foreach
之后或在foreach
内部的每个提取请求之后记录“new”吗?foreach之后的@connoraworden。这是因为我需要用ReactCool呈现列表,我添加了一个答案,这应该是您想要的。我认为OP的意思是在forEach
中的所有更新获取完成后打印“new”,所以这不起作用。如果OP可以确认您的怀疑,我很乐意更新我的答案@andyth代码看起来不错,但是数据
在末尾没有定义,并且“update”在“new”之后打印。我不确定返回的.forEach()
承诺是否有效。如果您是对的,您必须等待所有承诺得到解决。我编辑了答案代码这是一个优雅的解决方案。我学到了很多。非常感谢@connorawordenI。我很感谢你的详细回答,我投了更高的票。虽然它更难与我的代码结合。我已将其添加到我的原始帖子中,以便您可以看到它。
let queries = ["food", "movies", "news"]
let all = []
queries.forEach((keyword)=>{
let [subres] = await Promise.all([fetch(`https://reddit.com/r/${keyword}/hot.json?limit=100`).then((response) => response.json())]);
all.push(subres)
})
//now you can use the data globally or use the data to fetch more data
console.log(all)