Javascript 如何链接多个fetch()承诺?

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

下面的代码获取一个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的解决方案。但我认为它可以简化

谢谢你的回答

如何链接多个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)