【问题标题】:Async function inside for loop in ReactReact中for循环内的异步函数
【发布时间】:2019-05-17 16:03:08
【问题描述】:

我在 React 中有这段代码,它调用 Dark Sky API 并返回特定日期的天气数据。每个请求都有一个特定的日期。请求成功后,我通过 setState 将数据放入数组中。代码运行良好,但每个请求都是异步运行的,因此数据会按照每个调用完成的顺序推送到数组中,因此它不会让我得到一个有序的日期列表。代码如下:

loadPreviousMonth = async () => {
  const current = {...this.state};

  for (let i=30; i >= 1; i--) {
    const date = new Date();
    const previousDay = Math.floor(date.setDate(date.getDate() - i) / 1000);
    const url = `/api/darksky?latitude=${current.latitude}&longitude=${current.longitude},${previousDay}`;
    await darkSky(url, this.onTimeRequestSuccess, this.onError);
  }
}

谢谢!

【问题讨论】:

  • 您不应该将状态中的数据保存为无序,并在从状态检索后但在显示给用户之前对其进行排序。订购 30 个项目的数组只需几毫秒。但是只有在接收到当前的 api 后才进行下一次 api 调用将需要永远进行 30 次 api 调用。
  • 我可以这样做,但我希望我可以按顺序运行每个 API 调用,这样我就不必采取额外的步骤。
  • 你应该采取额外的步骤,编辑我之前的评论添加“为什么”。
  • 谢谢,我只是在 :) 之后对数组进行了排序

标签: javascript reactjs async-await


【解决方案1】:

您可以使用 for.. 其中将按顺序运行:

async function processArray(array) {
  for (const item of array) {
    await darkSky(url, this.onTimeRequestSuccess, this.onError);
  }
 console.log('Done!');
}

【讨论】:

    【解决方案2】:

    使用Promise.all。 Waaay 比在循环中使用递归或等待更快,因为所有 API 调用将同时发生而不是顺序发生。

    const promises = [];
    
    for (let i=30; i >= 1; i--) {
        const date = new Date();
        const previousDay = Math.floor(date.setDate(date.getDate() - i) / 1000);
        const url = `/api/darksky?latitude=${current.latitude}&longitude=${current.longitude},${previousDay}`;
        promises.push(darkSky(url, this.onTimeRequestSuccess, this.onError));
    }
    
    Promise.all(promises).then(arrOfResults => {
      // setState here
    });
    

    【讨论】:

      【解决方案3】:

      问题在于,没有什么可以确保您的一个请求在另一个请求之前完成,但这取决于函数darkSky 是如何实现的。据我可以从您的代码中猜到,它看起来像这样:

      function darkSky(url, onTimeRequestSuccess, onError) {
           return fecth(url).then(onTimeRequestSuccess).catch(onError);
      }
      

      因为您使用onTimeRequestSuccess 作为darkSky 中的promise 的解析,所以processArray 中的await 不会等待获取promise,而是等待onTimeRequestSuccess 的结果。这就是为什么不维护订单的原因。

      如果您想保持顺序(在获取数据后不重新排序)并且没有连续 30 次调用的链,一种方法可以是创建一个包含所有查询承诺的数组,等待所有查询使用Promise.all 然后将整个数组设置为状态。例如:

      loadPreviousMonth = () => {
        const current = {...this.state};
        const promises = [];
      
        for (let i=30; i >= 1; i--) {
          const date = new Date();
          const previousDay = Math.floor(date.setDate(date.getDate() - i) / 1000);
          const url = `/api/darksky?latitude=${current.latitude}&longitude=${current.longitude},${previousDay}`;
          promises.push(fetch(url));
        }
      
        Promise.all(promises).then((results) => this.setState({results: results}));
      }
      

      【讨论】:

        【解决方案4】:

        您可以尝试递归函数调用。像这样:

        var delay = ms => new Promise(res => setTimeout(res, ms));
        
        
        
            function executeInOrder(iteration){
                if(iteration>0){
                    delay(500).then(()=>{
                        console.log(iteration)
                        executeInOrder(iteration-1)
                    })
                }
            }
            
          executeInOrder(30)  
        但是正如cmets中提到的,调用all然后排序会更快。

        【讨论】:

          猜你喜欢
          • 2019-04-07
          • 2015-11-14
          • 2014-07-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-24
          • 1970-01-01
          相关资源
          最近更新 更多