【问题标题】:Run some code only after fetch statements inside of loops have finished executing仅在循环内的 fetch 语句执行完毕后运行一些代码
【发布时间】:2021-05-26 20:16:55
【问题描述】:

我正在根据来自 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.

【问题讨论】:

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


    【解决方案1】:

    在 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])
    
    
    

    【讨论】:

    • 说我在这些循环之前有更多代码,它们应该进入异步匿名函数内部吗? ||||关于你的评论 --> 最后一个函数不是异步的,它只是一个 React set 语句
    • 你可以把代码放在IIFE之前 使用IIFE只是一个小技巧,可以使用await
    • 和“setCompleteObjToDisplay”必须保留在 IIFE 内,以便按顺序执行?
    • 你应该把 async 放在外部函数之前。我犯了一个错误,没有必要使用异步 IIFE。如果外部函数是异步的,您可以使用 await 而无需将代码包装在 IIFE 中。代码会从上到下执行,因为你在每次异步调用之前都放了 await
    • 对不起,我忘记了常量。它应该与 const 一起使用
    【解决方案2】:

    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!
    
    })();
    

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

    【讨论】:

      猜你喜欢
      • 2018-11-27
      • 2015-07-19
      • 2020-11-21
      • 2021-06-04
      • 2021-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-27
      相关资源
      最近更新 更多