来自循环内的Promise值的setState

时间:2018-02-04 02:04:11

标签: javascript reactjs promise axios es6-promise

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

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

2 个答案:

答案 0 :(得分: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);
});

答案 1 :(得分:1)

这一特定要求被称为障碍。也就是说,您希望等待n个任务完成,然后执行某些操作。使用屏障可以实现“等待n个任务完成”部分。

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

如果您不想使用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变量。只要您使用箭头函数(this)而不是=>关键字,function范围就不会更改。
  2. 我缩进then结算的方式有点不同。那只是因为它对我来说似乎更具可读性