只有在循环内的 fetch 语句完成执行后才运行一些代码

时间:2021-02-24 13:42:42

标签: javascript reactjs async-await fetch use-effect

我正在根据来自 API 的两个不同请求构建一个对象。在任何给定时间,这些请求都使用三种不同的 ids。例如const ids = [1, 2, 3]

整个代码都在 useEffect 中,因为我希望它在每次 ids 更改时都运行。不确定这是否会影响代码执行顺序。

我希望我的程序按以下顺序运行:

  • 首先,我想运行请求以获取每个 ID 的“general_info”并将它们添加到对象中。
  • 其次,我想运行将每个 ID 与其余 ID 分别进行比较的请求,并将此信息添加到对象中。
  • 第三,我想仅在上述代码完成“合并到”此对象后运行一些代码。这就是我使用 React 的 UseState 钩子将 objCopy 设置为将要显示的对象的地方!

我很欣赏这应该处理异步请求,根据其他帖子,某些类型的循环似乎不适合于此。我不确定是使用 async await 还是简单地将 then() 一个接一个地链接起来。

代码类似:

useEffect(() => {

  // ... some other code ...

  const obj = {}

  // 1st loop
  ids.forEach(id =>{
     fetch(`api/general_info?id=${id}`)
     //whatever comes as response, merge into 'obj' 
  })

  // 2nd loop (to be run only after first loop has finished merging into obj)
  idsToCompare = [[1, 2], [1, 3], [2, 3]] // 

  idsToCompare.forEach(([id1, id2]) =>{
     fetch(`api/compare/?id1=${id1}&id2=${id2}`)
     //whatever comes as response, merge into 'obj' 
  })

  // 3 run this code ONLY AFTER the above loops have finished executing
  // (and obj merge is complete)!
  setCompleteObjToDisplay(obj) // React's setState
  console.log(obj) // complete obj!

}, [ids]) //every time ids change, this code should run.

2 个答案:

答案 0 :(得分:2)

在 foreach 中使用异步代码有点棘手。您可以改用 for in 并在外部函数之前添加异步

useEffect(() => {
(async () => {
  let obj = {} 

  for(const id in ids) {
    const res = await fetch(...)
    obj = {...obj, res}
  }

  idsToCompare = [[1, 2], [1, 3], [2, 3]]

  for(const [id1, id2] in idsToCompare) {
    const res = await fetch(...)
    obj = {...obj, res}
  }

  setCompleteObjToDisplay(obj) // I don't know if this function is asynchronous. Is so, add an await statement
  console.log(obj)
})(), [dependencies])


答案 1 :(得分:1)

Hugh 的回答是最好的解决方案,但我会添加其他信息:

另一种方法是使用 Promises 返回来控制主流程。

(async function() {

    const obj = {}

   await new Promise ((resolve, reject) => {
        // 1st loop
        ids.forEach(async (id) => {
            let data = await fetch(`api/general_info?id=${id}`)
            //whatever comes as response, merge into 'obj' 
            resolve(data);
        })
    })

    // 2nd loop (to be run only after first loop has finished merging into obj)
    idsToCompare = [[1, 2], [1, 3], [2, 3]] // 

    await new Promise ((resolve, reject) => {
        idsToCompare.forEach(([id1, id2]) =>{
            let data = await fetch(`api/compare/?id1=${id1}&id2=${id2}`)
            resolve(data);
            //whatever comes as response, merge into 'obj' 
        })
    })

    // 3 run this code ONLY AFTER the above loops have finished executing
    // (and obj merge is complete)!
    setCompleteObjToDisplay(obj)
    console.log(obj) // complete obj!

})();

这个例子不是使用 Promise 的最优雅的方式,但它很好地展示了它们是如何工作的 =)