循环内 Promise 值的 setState
setState from Promise values inside loop
我正在使用 React 和 axios 进行外部 API 调用,遍历传递回 API 调用的数组中的每个对象。在循环内部,我有一个承诺调用了另一个 returns 对象的函数。我想使用此对象返回的值并将它们分配给循环外的变量,该变量是用于设置状态的数组但我似乎无法这样做,因为它总是空的?希望下面我的代码中的评论能帮助您理解我的问题。
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);
});
}
.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 方式(注意 async
和 await
关键字):
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);
});
此特定要求称为障碍。也就是说,您想等到 n
个任务完成后,再做某事。 "wait till n number of tasks finish" 部分可以使用屏障来实现。
如果您使用 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);
});
一些旁注:
- 这里不需要
self
变量。 this
只要您使用箭头函数 (=>
) 而不是 function
关键字,范围就不会改变。
- 我缩进
then
解析的方式有点不同。那只是因为它对我来说更具可读性
我正在使用 React 和 axios 进行外部 API 调用,遍历传递回 API 调用的数组中的每个对象。在循环内部,我有一个承诺调用了另一个 returns 对象的函数。我想使用此对象返回的值并将它们分配给循环外的变量,该变量是用于设置状态的数组但我似乎无法这样做,因为它总是空的?希望下面我的代码中的评论能帮助您理解我的问题。
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);
});
}
.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 方式(注意 async
和 await
关键字):
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);
});
此特定要求称为障碍。也就是说,您想等到 n
个任务完成后,再做某事。 "wait till n number of tasks finish" 部分可以使用屏障来实现。
如果您使用 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);
});
一些旁注:
- 这里不需要
self
变量。this
只要您使用箭头函数 (=>
) 而不是function
关键字,范围就不会改变。 - 我缩进
then
解析的方式有点不同。那只是因为它对我来说更具可读性