如何在异步遍历中填充另一个异步调用的实际结果到对象属性
嘿,我看你遇到的是异步遍历里嵌套异步调用,导致对象属性拿到的是Promise而不是实际值的问题对吧?这可是异步代码里很常见的小坑,我来帮你捋清楚怎么解决。
首先得明确问题出在哪:你的getPlugin是个async函数,它本身返回的就是一个Promise,你直接把它赋值给cardDict['points'],自然就会得到Promise对象而不是最终的数值。而且你用map遍历的时候,没有等待每个异步调用完成,map本身也不会处理异步回调,所以最后结果里都是未取出实际值的Promise。
那怎么改呢?看下面的步骤:
给map的回调加上async,并用Promise.all等待所有异步操作完成
每个遍历项里的getPlugin都是异步的,我们需要让每个迭代都返回一个Promise,然后用Promise.all来等待所有这些Promise完成,这样才能拿到所有填充好数据的对象。在赋值points的时候await getPlugin的结果
既然getPlugin是async的,就得用await来取出它的最终返回值,这样cardDict['points']才会是你想要的具体数值,而不是Promise。别忘了把创建好的cardDict加入到cardList数组里
你原来的代码里创建了cardDict但没把它push到cardList,最后数组还是空的,这也是个小疏漏哦。
下面是修改后的完整代码片段,你可以对比着看:
elm.addEventListener('click', () => { return t.get('board', 'shared', 'bid') .then(data => { (async function () { const sheetId = data; const cardList = []; const mm = {}; const fetchCardData = `https://*******.execute-api.******.amazonaws.com/******/cardData?id=${boardId}`; const cd = await fetch(fetchCardData); let cardData = await cd.text(); cardData = JSON.parse(cardData); // 用Promise.all等待所有异步遍历完成 await Promise.all(cardData.map(async (x) => { const cardDict = {}; // await getPlugin拿到实际值 cardDict['points'] = await getPlugin(x.shortLink); cardDict['id'] = x.id; cardDict['title'] = x.name; // 把对象加入数组 cardList.push(cardDict); })); // 这里的cardList里的每个对象的points就是实际数值了 console.log(cardList); })(); }); });
另外,你的getPlugin函数里有个小问题:typeof pluginData[0] != undefined这个判断逻辑不对,typeof返回的是字符串,应该改成pluginData[0] !== undefined,或者typeof pluginData[0] !== 'undefined',这样才能正确判断是否存在值,避免潜在错误。
修改后的getPlugin可以写成这样:
async function getPlugin(id) { const fetchCardPlugin = `https://*********..amazonaws.com/*****/cardPlugin?id=${id}`; const cp = await fetch(fetchCardPlugin); let pluginData = await cp.text(); pluginData = JSON.parse(pluginData); // 修正判断逻辑 if (pluginData[0] !== undefined) { return pluginData[0]['value'].split(':').pop().split('')[0]; } else { return ''; } }
这样改完之后,你的cardList里每个对象的points就会是实际的数值,而不是Promise对象啦。
备注:内容来源于stack exchange,提问作者Paul Carlson

