React Promise 异步任务顺序不正确
React Promise asynchronous tasks order not correct
我正在使用 Promise 进行多次调用。
我的 API 要获取的端点是:
- https://www.api-football.com/demo/v2/statistics/357/5/2019-08-30
- https://www.api-football.com/demo/v2/statistics/357/5/2019-09-30
- https://www.api-football.com/demo/v2/statistics/357/5/2019-10-30
见代码
export function getTeamsStats(league, team, type) {
return function(dispatch) {
const url = "https://www.api-football.com/demo/v2/statistics";
let dates = ["2019-08-30", "2019-09-30", "2019-10-30"];
const getAllData = (dates, i) => {
return Promise.allSettled(dates.map(x => url + '/' + 357 + '/' + 5 + '/' + x).map(fetchData));
}
const fetchData = (URL) => {
return axios
.get(URL)
.then(res => {
const {
matchsPlayed: { total: teamsTotalMatchsPlayed},
} = res.data.api.statistics.matchs;
const matchsPlayed = teamsTotalMatchsPlayed;
dispatch(receivedTeamsStat(matchsPlayed, type));
})
.catch(e => {
console.log(e);
});
}
getAllData(dates).then(resp=>{console.log(resp)}).catch(e=>{console.log(e)})
}
}
然后在我的组件中,我将这个特定球队(在本例中为圣保罗)从初始日期 30-8-2019 到 30-10-2019
const [dataHomeTeam, setDataHomeTeam] = useState([]);
useEffect(() => {
if (!team.matchsPlayed) {
return ;
}
setDataHomeTeam(prev =>
prev.concat([
{
matches: team.matchsPlayed,
}
])
);
},[team.matchsPlayed]);
console.log('Data Array', dataHomeTeam);
问题是,通常在页面的第一次呈现中,我的匹配顺序是正确的,从 30-8-2019 到 30-10-2019
查看控制台日志图像
但有时不是,请看这里
所以问题是,我如何确保 Promise 以正确的顺序返回请求?
我正在使用Promise.allSettled
,多个不依赖于彼此的异步任务成功完成,但顺序并不总是正确的。
Promise.all 保留顺序。您应该先等待所有 api 承诺解决,然后再进行操作分配。
有用的文章:
怎么样,你做到了,
function updateUI(value) {
console.log(value);
// Do something to update the UI.
}
// Note that order of resolution of Promises is 2, 1, 3
const promise1 = new Promise((resolve) => setTimeout(resolve, 200, 1)).then(updateUI);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 2)).then(updateUI);
const promise3 = new Promise((resolve) => setTimeout(resolve, 300, 3)).then(updateUI);
const promises = [promise1, promise2, promise3];
Promise.allSettled(promises).
then((value) => console.log('Nothing to do here', value));
// Output: 2, 1, 3
// Here we update the UI as soon as the result is obtained. As a result, the UI is also updated in the
// order in which the promise was resolved.
换句话说,我们不仅要等待网络调用,还要等待网络调用和 UI 更新完成每个您不想要的 ID。
你应该怎么做,
// Note that order of resolution of Promises is 2, 1, 3 (Same as previous)
const promise1 = new Promise((resolve) => setTimeout(resolve, 200, 1));
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 2));
const promise3 = new Promise((resolve) => setTimeout(resolve, 300, 3));
const promises = [promise1, promise2, promise3];
Promise.allSettled(promises).
then((results) => results.forEach((result) => updateUI(result.value)));
// Output: 1, 2, 3
// Here, we wait for all the network requests to complete and then loop through the results and update the UI.
// This ensures that the result is in order.
如果您不想等待所有 Promise 解析并希望在一个解析后立即更新 UI 并仍然保持顺序,那么您需要传递元素在数组,然后使用该位置更新数组中给定位置的元素。
希望对您有所帮助。
这应该通过 Promise.all 解决,因为如文档中所述
Returned values will be in order of the Promises passed, regardless of
completion order.
您遇到的问题是,您正在通过根据每个承诺的完成调用 dispatch
来设置状态,因为承诺可以在任何给定时间完成(例如,第三个请求可以先完成,因为它们已发送同时),dispatch
将被调用,并且您的状态管理会将其设置为第一响应(这就是为什么您“有时”会出现这种行为,因为这取决于网络谁先完成)。
这意味着您要么更改 dispatch
以接收已完成承诺的数组,要么在它们完成后调用 dispatch
one-by-one,如下面的代码所示 -解决所有问题并按顺序发送:
export function getTeamsStats(league, team, type) {
return function (dispatch) {
const url = "https://www.api-football.com/demo/v2/statistics";
let dates = ["2019-08-30", "2019-09-30", "2019-10-30"];
const getAllData = (dates, i) => {
return Promise.all(dates.map(x => url + '/' + 357 + '/' + 5 + '/' + x).map(fetchData));
}
const fetchData = (URL) => {
return axios
.get(URL)
.then(res => {
const {
matchsPlayed: { total: teamsTotalMatchsPlayed },
} = res.data.api.statistics.matchs;
return teamsTotalMatchsPlayed;
})
.catch(e => {
console.log(e);
});
}
getAllData(dates).then(resp => {
console.log(resp)
// 'resp' here are all 'teamsTotalMatchsPlayed' in correct order (here I mean order of call, not promise completion)
// so just dispatch them in order
resp.map(matchsPlayed => receivedTeamsStat(matchsPlayed, type));
}).catch(e => {
console.log(e)
})
}
}
请注意,我可能犯了一些语法错误,但你明白了。
export function getTeamsStats(league, team, type) {
return function (dispatch) {
const URL = "https://www.api-football.com/demo/v2/statistics";
let DATES = ["2019-08-30", "2019-09-30", "2019-10-30"];
return Promise.all(
DATES.map(date => axios.get(`${URL}/357/5/${date}`))
.then(responseList => {
responseList.map((res) => {
const {
matchsPlayed: { total: teamsTotalMatchsPlayed },
} = res.data.api.statistics.matchs;
const matchsPlayed = teamsTotalMatchsPlayed;
dispatch(receivedTeamsStat(matchsPlayed, type));
});
})
.catch((e) => console.log(e))
);
};
}
实现您的要求的理想方式是使用 Promise.all()。主要有两个原因,
- 无论完成顺序如何,以 Promise 传递的顺序维护 return 值。
Returned values will be in order of the Promises passed, regardless of
completion order.
参考Return值部分在link
- 如果可迭代对象中的任何承诺被拒绝,则拒绝 returned 承诺(短路)。
这也很重要。我们不需要等待所有异步可迭代承诺成为 resolved/rejected,如果 fetchData 的第一个可迭代承诺拒绝我们可以短路并拒绝 return承诺。
另一方面Promise.allSettled(),
Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an
array of objects that each describes the outcome of each promise.
它也不维护 returned 承诺中的顺序。
Promise.allSettled() 方法从不 short-circuits。永远兑现承诺,从不拒绝。
参考下面tablePromise.all()和Promise.allSettled()的比较,
<script src="https://gist.github.com/Seralahthan/9934ba2bd185a8ccfbdd8e4b3523ea23.js"></script>
我正在使用 Promise 进行多次调用。
我的 API 要获取的端点是:
- https://www.api-football.com/demo/v2/statistics/357/5/2019-08-30
- https://www.api-football.com/demo/v2/statistics/357/5/2019-09-30
- https://www.api-football.com/demo/v2/statistics/357/5/2019-10-30
见代码
export function getTeamsStats(league, team, type) {
return function(dispatch) {
const url = "https://www.api-football.com/demo/v2/statistics";
let dates = ["2019-08-30", "2019-09-30", "2019-10-30"];
const getAllData = (dates, i) => {
return Promise.allSettled(dates.map(x => url + '/' + 357 + '/' + 5 + '/' + x).map(fetchData));
}
const fetchData = (URL) => {
return axios
.get(URL)
.then(res => {
const {
matchsPlayed: { total: teamsTotalMatchsPlayed},
} = res.data.api.statistics.matchs;
const matchsPlayed = teamsTotalMatchsPlayed;
dispatch(receivedTeamsStat(matchsPlayed, type));
})
.catch(e => {
console.log(e);
});
}
getAllData(dates).then(resp=>{console.log(resp)}).catch(e=>{console.log(e)})
}
}
然后在我的组件中,我将这个特定球队(在本例中为圣保罗)从初始日期 30-8-2019 到 30-10-2019
const [dataHomeTeam, setDataHomeTeam] = useState([]);
useEffect(() => {
if (!team.matchsPlayed) {
return ;
}
setDataHomeTeam(prev =>
prev.concat([
{
matches: team.matchsPlayed,
}
])
);
},[team.matchsPlayed]);
console.log('Data Array', dataHomeTeam);
问题是,通常在页面的第一次呈现中,我的匹配顺序是正确的,从 30-8-2019 到 30-10-2019
查看控制台日志图像
但有时不是,请看这里
所以问题是,我如何确保 Promise 以正确的顺序返回请求?
我正在使用Promise.allSettled
,多个不依赖于彼此的异步任务成功完成,但顺序并不总是正确的。
Promise.all 保留顺序。您应该先等待所有 api 承诺解决,然后再进行操作分配。
有用的文章:
怎么样,你做到了,
function updateUI(value) {
console.log(value);
// Do something to update the UI.
}
// Note that order of resolution of Promises is 2, 1, 3
const promise1 = new Promise((resolve) => setTimeout(resolve, 200, 1)).then(updateUI);
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 2)).then(updateUI);
const promise3 = new Promise((resolve) => setTimeout(resolve, 300, 3)).then(updateUI);
const promises = [promise1, promise2, promise3];
Promise.allSettled(promises).
then((value) => console.log('Nothing to do here', value));
// Output: 2, 1, 3
// Here we update the UI as soon as the result is obtained. As a result, the UI is also updated in the
// order in which the promise was resolved.
换句话说,我们不仅要等待网络调用,还要等待网络调用和 UI 更新完成每个您不想要的 ID。
你应该怎么做,
// Note that order of resolution of Promises is 2, 1, 3 (Same as previous)
const promise1 = new Promise((resolve) => setTimeout(resolve, 200, 1));
const promise2 = new Promise((resolve) => setTimeout(resolve, 100, 2));
const promise3 = new Promise((resolve) => setTimeout(resolve, 300, 3));
const promises = [promise1, promise2, promise3];
Promise.allSettled(promises).
then((results) => results.forEach((result) => updateUI(result.value)));
// Output: 1, 2, 3
// Here, we wait for all the network requests to complete and then loop through the results and update the UI.
// This ensures that the result is in order.
如果您不想等待所有 Promise 解析并希望在一个解析后立即更新 UI 并仍然保持顺序,那么您需要传递元素在数组,然后使用该位置更新数组中给定位置的元素。
希望对您有所帮助。
这应该通过 Promise.all 解决,因为如文档中所述
Returned values will be in order of the Promises passed, regardless of completion order.
您遇到的问题是,您正在通过根据每个承诺的完成调用 dispatch
来设置状态,因为承诺可以在任何给定时间完成(例如,第三个请求可以先完成,因为它们已发送同时),dispatch
将被调用,并且您的状态管理会将其设置为第一响应(这就是为什么您“有时”会出现这种行为,因为这取决于网络谁先完成)。
这意味着您要么更改 dispatch
以接收已完成承诺的数组,要么在它们完成后调用 dispatch
one-by-one,如下面的代码所示 -解决所有问题并按顺序发送:
export function getTeamsStats(league, team, type) {
return function (dispatch) {
const url = "https://www.api-football.com/demo/v2/statistics";
let dates = ["2019-08-30", "2019-09-30", "2019-10-30"];
const getAllData = (dates, i) => {
return Promise.all(dates.map(x => url + '/' + 357 + '/' + 5 + '/' + x).map(fetchData));
}
const fetchData = (URL) => {
return axios
.get(URL)
.then(res => {
const {
matchsPlayed: { total: teamsTotalMatchsPlayed },
} = res.data.api.statistics.matchs;
return teamsTotalMatchsPlayed;
})
.catch(e => {
console.log(e);
});
}
getAllData(dates).then(resp => {
console.log(resp)
// 'resp' here are all 'teamsTotalMatchsPlayed' in correct order (here I mean order of call, not promise completion)
// so just dispatch them in order
resp.map(matchsPlayed => receivedTeamsStat(matchsPlayed, type));
}).catch(e => {
console.log(e)
})
}
}
请注意,我可能犯了一些语法错误,但你明白了。
export function getTeamsStats(league, team, type) {
return function (dispatch) {
const URL = "https://www.api-football.com/demo/v2/statistics";
let DATES = ["2019-08-30", "2019-09-30", "2019-10-30"];
return Promise.all(
DATES.map(date => axios.get(`${URL}/357/5/${date}`))
.then(responseList => {
responseList.map((res) => {
const {
matchsPlayed: { total: teamsTotalMatchsPlayed },
} = res.data.api.statistics.matchs;
const matchsPlayed = teamsTotalMatchsPlayed;
dispatch(receivedTeamsStat(matchsPlayed, type));
});
})
.catch((e) => console.log(e))
);
};
}
实现您的要求的理想方式是使用 Promise.all()。主要有两个原因,
- 无论完成顺序如何,以 Promise 传递的顺序维护 return 值。
Returned values will be in order of the Promises passed, regardless of completion order.
参考Return值部分在link
- 如果可迭代对象中的任何承诺被拒绝,则拒绝 returned 承诺(短路)。
这也很重要。我们不需要等待所有异步可迭代承诺成为 resolved/rejected,如果 fetchData 的第一个可迭代承诺拒绝我们可以短路并拒绝 return承诺。
另一方面Promise.allSettled(),
Promise.allSettled() method returns a promise that resolves after all of the given promises have either fulfilled or rejected, with an array of objects that each describes the outcome of each promise.
它也不维护 returned 承诺中的顺序。
Promise.allSettled() 方法从不 short-circuits。永远兑现承诺,从不拒绝。
参考下面tablePromise.all()和Promise.allSettled()的比较,
<script src="https://gist.github.com/Seralahthan/9934ba2bd185a8ccfbdd8e4b3523ea23.js"></script>