链式嵌套Promise结合For Loop获取游戏属性后无法拿到更新对象求助
嘿,我看你在链式Promise里处理游戏属性的时候遇到了数据更新后拿不到的问题,这其实是异步操作的特性导致的——你打印games的时候,那些异步获取属性的调用还没完成呢!咱们先理清楚问题根源,再给出两种靠谱的解决方案。
先还原你的问题场景
你原来的代码大概是这样的(补全了截断的部分):
var games = [];
window.addEventListener('load', function() {
// 检查网络并获取智能合约地址
checkNetworkAndGetContractAddress()
.then(contractAddr => {
// 注册包含所有游戏地址的合约
return registerGameListContract(contractAddr);
})
.then(gameListContract => {
// 获取游戏数量
return gameListContract.getGameCount();
})
.then(gameCount => {
// 遍历每个游戏
for (let i = 0; i < gameCount; i++) {
let game = {};
games.push(game);
// 异步获取各项属性
getGameName(i).then(name => game.name = name);
getGameScore(i).then(score => game.score = score);
getGameCreator(i).then(creator => game.creator = creator);
}
// 这里打印games,发现属性都是undefined
console.log(games);
})
.catch(err => console.error(err));
});
问题出在:for循环会快速跑完所有迭代,发起所有异步请求,但这些请求的.then回调要等异步操作完成后才会执行——你在循环结束后立刻打印games,此时所有属性还没被赋值,自然拿不到更新后的对象。
解决方案1:并行处理(推荐,效率更高)
用Promise.all来等待所有异步操作完成,同时并行发起请求节省时间:
var games = []; window.addEventListener('load', function() { checkNetworkAndGetContractAddress() .then(contractAddr => registerGameListContract(contractAddr)) .then(gameListContract => gameListContract.getGameCount()) .then(gameCount => { // 收集每个游戏的处理Promise const gamePromises = []; for (let i = 0; i < gameCount; i++) { // 并行获取单个游戏的所有属性,组装成完整对象 const singleGamePromise = Promise.all([ getGameName(i), getGameScore(i), getGameCreator(i) ]).then(([name, score, creator]) => ({ name, score, creator })); gamePromises.push(singleGamePromise); } // 等待所有游戏的Promise都完成 return Promise.all(gamePromises); }) .then(completedGames => { games = completedGames; // 这里就能拿到所有带完整属性的游戏了! console.log(games); }) .catch(err => console.error('操作出错:', err)); });
这个方案的核心逻辑:
- 对每个游戏,用
Promise.all并行发起所有属性请求,确保该游戏的所有属性都获取到后再组装成对象 - 把所有游戏的处理Promise收集到数组,再用外层的
Promise.all等待所有游戏都处理完成 - 只有当所有异步操作都结束后,才会进入最后的
.then回调,此时的completedGames就是完整的游戏数据数组
解决方案2:串行处理(适合有接口调用限制的场景)
如果你的接口有调用频率限制,不能并行发起太多请求,可以用async/await把异步代码写成同步风格,逐个处理游戏:
var games = []; window.addEventListener('load', async function() { try { // 按顺序完成前置步骤 const contractAddr = await checkNetworkAndGetContractAddress(); const gameListContract = await registerGameListContract(contractAddr); const gameCount = await gameListContract.getGameCount(); // 逐个处理每个游戏,串行获取属性 for (let i = 0; i < gameCount; i++) { const name = await getGameName(i); const score = await getGameScore(i); const creator = await getGameCreator(i); games.push({ name, score, creator }); } // 所有异步操作完成后再打印 console.log(games); } catch (err) { console.error('操作出错:', err); } });
这个方案的核心逻辑:
- 用
async/await把异步操作转换成同步写法,逻辑更直观 - 每个游戏的属性请求依次执行,前一个完成后才会执行下一个,避免触发接口频率限制
- 用
try/catch捕获整个流程中的错误,和Promise的.catch效果一致
关键要点总结
- 异步操作不会阻塞代码执行,必须等待所有需要的异步操作完成后,才能访问它们的结果
Promise.all是处理多个并行异步操作的最佳选择,能大幅提升效率async/await让复杂的异步流程更易读,是现代JavaScript处理异步的主流方式
内容的提问来源于stack exchange,提问作者Mike

