以下代码获取一个json列表,然后对每个列表项进行另一个访存调用以更改其值。问题是它没有同步完成。在“更新”之前,将“新”打印到控制台。
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的解决方案。但我认为可以简化。
感谢您的回答。
答案 0 :(得分:2)
解决此问题的最佳方法是使用Promise.all()
和map()
。
在这种情况下,什么地图将返回fetch
的所有承诺。
然后发生的情况是await
将使您的代码同步执行,因为它将在继续执行之前等待所有诺言得到解决。
此处使用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)
});
答案 1 :(得分:0)
如何链接多个fetch()承诺?
您可以像以前一样进行操作,只需添加另一个.then()
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");
})
.then(()=>{
console.log("new", data)
})
});
});
答案 2 :(得分:0)
如果只想显示一次“ 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)
}
})
});
});
答案 3 :(得分:0)
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))
答案 4 :(得分:0)
您不应该在这里使用forEach
。最好的解决方案是使用Promise.all
来等待所有承诺的数组(fetch
是一个承诺)进行所有解析,然后才能处理数据。
在这里,我用一些示例数据创建了一个虚拟提取功能,以快速向您展示其工作原理。
const dummyObj = {
main: [ { id: 1 }, { id: 2 }, { id: 5 } ],
other: {
1: 'data1',
2: 'data2',
3: 'data3',
4: 'data4',
5: 'data5',
6: 'data6',
7: 'data7',
}
}
// The summy function simply returns a subset of the sample
// data depending on the type and id params after 2 seconds
// to mimic an API call
function dummyFetch(type, id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(id ? dummyObj[type][id] : dummyObj[type]);
}, 2000);
});
}
// In the first fetch we display the data, just
// like you did in your example
dummyFetch('main')
.then(data => {
console.log("old", data);
return data;
})
.then(data => {
// Instead of a forEach use Array.map to iterate over the
// data and create a new fetch for each
const promises = data.map(o => dummyFetch('other', o.id));
// You can then wait for all promises to be resolved
Promise.all(promises).then((data) => {
// Here you would iterate over the returned group data
// (as in your example)
// I'm just logging the new data as a string
console.log(JSON.stringify(data));
// And, finally, there's the new log at the end
console.log("new", data)
});
});
这是async/await
版本:
const dummyObj = {
main: [ { id: 1 }, { id: 2 }, { id: 5 } ],
other: {
1: 'data1',
2: 'data2',
3: 'data3',
4: 'data4',
5: 'data5',
6: 'data6',
7: 'data7',
}
}
function dummyFetch(type, id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(id ? dummyObj[type][id] : dummyObj[type]);
}, 2000);
});
}
(async () => {
const oldData = await dummyFetch('main');
console.log("old", oldData);
const promises = oldData.map(o => dummyFetch('other', o.id));
const newData = await Promise.all(promises);
console.log(JSON.stringify(newData));
console.log('new', newData);
})();
答案 5 :(得分:0)
在一个阵列中存储多个响应
以下代码在查询中获取多个关键字,并将所有三个响应的所有响应存储到 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)
答案 6 :(得分:-1)
您将需要一个递归函数来完成此操作。
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)
})
}
注意:这是未经测试的代码。