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

时间:2019-07-17 11:44:42

标签: 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,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的解决方案。但我认为可以简化。

感谢您的回答。

7 个答案:

答案 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)
        })
}

注意:这是未经测试的代码。