【问题标题】:setState from Promise values inside loop来自循环内的 Promise 值的 setState
【发布时间】:2018-07-14 05:05:42
【问题描述】:

我正在使用带有 axios 的 React 进行外部 API 调用,循环遍历数组中的每个对象,并传回 API 调用。在循环内部,我有一个承诺,它调用了另一个返回对象的函数。我想使用这个对象返回的值,并将它们分配给循环外的一个变量,该变量是一个用于设置状态的数组,但我似乎不能这样做,因为它总是空的?希望我下面代码中的 cmets 可以帮助您理解我的问题。

let self = this;
this.instance.get('/fixtures?timeFrame=n1').then((fixtures) => {
  // get all fixtures
  const allFixtures = fixtures.data.fixtures;
  // create valid fixtures array to add all fixture details to pass to fixtures state
  let validFixtures = [];
  // loop through all fixture objects in allFixtures array
  for (var i = 0; i < (allFixtures.length); i++) {
    // check if valid fixture, returns true or false
    let isValid = self.isValid(allFixtures[i]);
    // if fixture is valid
    if (isValid) {
      // get id of fixture to pass through to fixture route with id query
      let fixtureId = allFixtures[i]._links.self.href.split('v1/')
        .pop();
      // home teams name
      let homeTeam = allFixtures[i].homeTeamName;
      // away teams name
      let awayTeam = allFixtures[i].awayTeamName;
      // call head2head function to get all previous results from the two teams playing and returns average score
      // returns as object, example: { 'homeTeamAvgScore': 2, 'awayTeamAvgScore': 1 }
      self.getHead2Head(fixtureId, homeTeam,
        awayTeam).then((avg) => {
        //in here i want to push object into validFixtures array along with homeTeam and awayTeam as named values
        return validFixtures.push({
          'homeTeam': homeTeam,
          'awayTeam': awayTeam,
          'homeTeamAvgScore': avg.homeTeamAvgScore,
          'awayTeamAvgScore': avg.awayTeamAvgScore
        })
      });
    }
  }
  //validFixtures is empty??? 
  //How else can push to array and then later setState to fixtures with validFixtures array???
  self.setState({
    fixtures: validFixtures
  });
}).catch((error) => {
  console.log(error);
});
}

【问题讨论】:

  • 在self.getHead2Head前加一个“return”关键字,应该可以解决你的问题
  • @GaneshKarewad 这似乎在第三次迭代后停止循环。 allFixtures数组的长度是34但只循环了3次?
  • 应该是 return Promise.all(allFixtures,(fixture) =>{ // let homeTeam = fixture.homeTeam let awayTeam = fixture.awayTeam return self.getHead2Head(fixtureId, homeTeam, awayTeam).then ((avg) => { return validFixtures.push({ 'homeTeam': homeTeam, 'awayTeam': awayTeam, 'homeTeamAvgScore': avg.homeTeamAvgScore, 'awayTeamAvgScore': avg.awayTeamAvgScore }) }); }) 你可能会必须修改此代码,因为我在没有 js 编译器或解释器的手机上回答这个问题。希望对你有帮助
  • 长话短说使用 return promise.all 而不是 for 循环

标签: javascript reactjs promise axios es6-promise


【解决方案1】:

.then 处理程序总是被异步调用。因此,在您的情况下,validFixtures.push() 将比self.setState({ fixtures: validFixtures }); 稍后执行(原文如此!)

如何解决:

1) 旧的 JS 方式。

  let validFixtures = [];
  let promieses = [];
  for (...) {
    ...
      promises.push(self.getHead2Head(fixtureId, homeTeam,
        awayTeam).then((avg) => {
        //in here i want to push object into validFixtures array along with homeTeam and awayTeam as named values
        return validFixtures.push({
          'homeTeam': homeTeam,
          'awayTeam': awayTeam,
          'homeTeamAvgScore': avg.homeTeamAvgScore,
          'awayTeamAvgScore': avg.awayTeamAvgScore
        })
      }));
    ...
  }

  Promise.all(promises).then(() => {
    self.setState({
      fixtures: validFixtures
    });
  });

2)现代JS方式(注意asyncawait关键字):

let self = this;
this.instance.get('/fixtures?timeFrame=n1').then(async (fixtures) => {
    // get all fixtures
    const allFixtures = fixtures.data.fixtures;
    // create valid fixtures array to add all fixture details to pass to fixtures state
    let validFixtures = [];
    // loop through all fixture objects in allFixtures array
    for (var i = 0; i < (allFixtures.length); i++) {
        // check if valid fixture, returns true or false
        let isValid = self.isValid(allFixtures[i]);
        // if fixture is valid
        if (isValid) {
            // get id of fixture to pass through to fixture route with id query
            let fixtureId = allFixtures[i]._links.self.href.split('v1/')
                .pop();
            // home teams name
            let homeTeam = allFixtures[i].homeTeamName;
            // away teams name
            let awayTeam = allFixtures[i].awayTeamName;
            // call head2head function to get all previous results from the two teams playing and returns average score
            // returns as object, example: { 'homeTeamAvgScore': 2, 'awayTeamAvgScore': 1 }
            const avg = await self.getHead2Head(fixtureId, homeTeam, awayTeam);
            //in here i want to push object into validFixtures array along with homeTeam and awayTeam as named values
            validFixtures.push({
                'homeTeam': homeTeam,
                'awayTeam': awayTeam,
                'homeTeamAvgScore': avg.homeTeamAvgScore,
                'awayTeamAvgScore': avg.awayTeamAvgScore
            });
        }
    }
    //validFixtures is empty??? 
    //How else can push to array and then later setState to fixtures with validFixtures array???
    self.setState({
        fixtures: validFixtures
    });
}).catch((error) => {
    console.log(error);
});

【讨论】:

    【解决方案2】:

    此特定要求称为障碍。也就是说,你要等到n 的任务数完成后,再做一些事情。使用屏障可以实现“等待 n 个任务完成”部分。

    如果您使用 Promises,则可以使用 Promise.all 轻松完成。 Axios 公开了 Promise 接口。

    如果你不想使用 Promises,你要么必须使用类似 async npm 库的东西,要么自己实现一个屏障。

    更新:

    Async - Await 与其他答案之一中提到的Promise.all 不同。建议的方法会降低性能,因为循环将一个接一个地同步运行。这在MDN docs 中有清楚的解释。

    示例修复,

    this.instance.get('/fixtures?timeFrame=n1')
        .then((fixtures) => {
            // ...Same code as yours
            const allFixtures = fixtures.data.fixtures;
            let promises = [];
    
            for (let i = 0; i < (allFixtures.length); i++) {
                // ... Same code as yours
                if (isValid) {
                    // ... Same code as yours
    
                    // Don't call "then". We will resolve these promises later
                    promises.push(this.getHead2Head(fixtureId, homeTeam, awayTeam));
                }
            }
    
            Promise.all(promises)
                .then(averages=>{
                    let validFixtures = averages.map((avg, index)=>{
                        return {
                            'homeTeam': allFixtures[index].homeTeamName,
                            'awayTeam': allFixtures[index].awayTeamName,
                            'homeTeamAvgScore': avg.homeTeamAvgScore,
                            'awayTeamAvgScore': avg.awayTeamAvgScore
                        };
                    });
                    this.setState({
                        fixtures: validFixtures
                    });
                });
        })
        .catch((error) => {
            console.log(error);
        });
    

    附注:

    1. 这里不需要self 变量。只要您使用箭头函数 (=&gt;) 而不是 function 关键字,this 的作用域就不会改变。
    2. 我缩进then 解析的方式有点不同。那只是因为它对我来说似乎更具可读性

    【讨论】:

    • 感谢您的回答,我仍然有点不确定我需要在哪里准确地执行 Promise.all,它是不是 @Ganesh Karewad 建议的 for 循环?
    • 谢谢你,这真的帮助我更多地理解了 Promise!
    • 唯一的问题是,例如,for 循环的第一次迭代无效,而第二次迭代是,在执行 averages 的映射时,0 的索引将等于第一次迭代,即使它无效。
    猜你喜欢
    • 2017-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-15
    • 1970-01-01
    • 2021-09-09
    相关资源
    最近更新 更多