for循环请求接口异步怎么赋值呢?

需求:for循环数组arr调用同一接口传入不同 id 获取图片路径赋值到url上,再用arr渲染列表
遇到的问题:getUrl()中怎么把res返回出来,还有getUrl异步的是不是不能直接i.url = getUrl(i.id)
请巨佬指点一下

arr = [
    {
        id:'xxx'
        name:"xxx",
        url:""
    },
    {
        id:'xxx'
        name:"xxx",
        url:""
    }
]

function getUrl(id) {
  getFileUrl(id).then(res=>{
        console.log(res) // 图片路径
  })
}

arr.forEach(i=>{
    i.url = getUrl(i.id)
})
阅读 2.8k
2 个回答

异步已经脱离了原来的执行顺序,需要在新的“异步”顺序中执行代码。建议你先看看 ——

异步编程需要“意识” - SegmentFault 思否

既然 getFileUrl 是一个异步方法,可以使用回调或者 Promise 来组织“异步顺序”。如果想按同步的写法来写,可以使用 async/await 语法,参考:

理解 JavaScript 的 async/await - SegmentFault 思否

就这个问题,看 getFileUrl 的写法,应该是返回的一个 Promise,不需要再封一个 getUrl,可以改写代码如下(大致)

for (const it of arr) {
    getFileUrl(it.id).then(url => it.url = url);
}

或者使用 await 写法,下面这种写法不会同时发起异步处理,会一个一个的去发起并处理完成。但 IIFE 本身会立即返回(返回的是一个 Promise,丢掉了,没使用)

(async () => {
    for (const it of arr) {
        it.url = await getFileUrl(it.id);
    }

})();

虽然一般处理异步的循环都应该使用 for ... in/of,而不使用 forEach(),但这里因为你不需要等全部返回,所以用 forEach 也是可以的

arr.forEach(it => getFileUrl(it.id).then(url => it.url = url));

如果要封 getUrl,可以

function getUrl(model) {
   getFileUrl(model.id).then(url => model.url = url);
}

arr.forEach(getUrl);

最后,如果要等所有地址取完,除了上使用了 await 的 for ... of 之外,也可以使用 Promise.all 或 Promise.allSettled 来等待完成

(async () => {
    const promises = arr.map(async it => it.url = await getFileUrl(it.id));
    await Promise.all(promises);
    // 然后干别的事情
})();

ps:如果是我司处理这个需求,就直接要求接口支持一次查询多个id的图片地址。这才是靠谱的逻辑。

单个可以这样

arr.forEach(async i=>{
  i.url = await getFileUrl(i.id)
})
推荐问题
宣传栏