背景
今年因为忙(其实是懒),没怎么写博客,加上工作内容比较low 遇到的问题也low,实在没啥好写的,这次终于碰上了个有趣的问题 赶紧来写一波
资料
背景
在日常的项目开发中,常常遇到批量删除的应用场景,比如批量删除人员信息、设备信息、时间计划等。但是协议常常只给了单个删除的接口,而且设备那边同时只能处理一个相同的接口,所以使用异步+循环处理势在必行。本文就在讨论如何解决这个业务问题
问题
一开始我采用的是下面这种循环里嵌套异步处理,但是经过测试发现,deleteIOTChannel
这条接口,实际上上条请求发出后,并未等待返回,下条请求就已经发出,所以下面这种在map循环里进行await 实际上是同步操作的
aIdList.map(async (item) => {
await _WebSDK.WSDK_SetDeviceConfig(
'deleteIOTChannel',
{ channel: item },
{
type: 'DELETE'
}
);
});
查阅资料
按理来说,循环里面嵌套异步处理,应当是完全异步的,但是现在变成了同步操作,是不是和map()
循环的原理有关系,我查阅了资料之后总结出以下知识点
1.map循环的异步处理
如果在map中使用await, map
始终返回promise数组,这是因为异步函数总是返回promise
。
const mapLoop = async _ => {
console.log('Start')
const numFruits = await fruitsToGet.map(async fruit => {
const numFruit = await getNumFruit(fruit);
return numFruit;
})
console.log(numFruits);
console.log('End')
}
“Start”;
“[Promise, Promise, Promise]”;
“End”;
如果你在 map 中使用 await,map 总是返回promises,你必须等待promises 数组得到处理。 或者通过await Promise.all(arrayOfPromises)
来完成此操作。
const mapLoop = async _ => {
console.log('Start');
const promises = fruitsToGet.map(async fruit => {
const numFruit = await getNumFruit(fruit);
return numFruit;
});
const numFruits = await Promise.all(promises);
console.log(numFruits);
console.log('End')
}
2.for循环的异步处理
在for循环中,过上使用getNumFruit
来获取每个水果的数量,并将数量打印到控制台。
由于getNumFruit
返回一个promise,我们使用 await 来等待结果的返回并打印它。
const forLoop = async _ => {
console.log('start');
for (let index = 0; index < fruitsToGet.length; index ++) {
const fruit = fruitsToGet[index];
const numFruit = await getNumFruit(fruit);
console.log(numFruit);
}
console.log('End')
}
当使用await时,希望JavaScript暂停执行,直到等待 promise 返回处理结果。这意味着for循环中的await 应该按顺序执行。
结果正如你所预料的那样。
“Start”;
“Apple: 27”;
“Grape: 0”;
“Pear: 14”;
“End”;
这种行为适用于大多数循环(比如while和for-of循环)…
解决方案
由资料可得,异步处理写在map里还需要再处理一次才可以,所以我们采用正确写法是:在for循环里 才是真正的异步
for (let item in aIdList) {
try {
await _WebSDK.WSDK_SetDeviceConfig(
'deleteIOTChannel',
{ channel: item },
{
type: 'DELETE',
success: () => {},
error: (xml, xhr) => {
_oResponse.saveState(xhr);
}
}
);
} catch (error) {}
}
在项目中实际测试,确实实现了异步处理,请求在上一个请求返回后才进行请求。
总结
从上面看出来什么
如果你想连续执行await调用,请使用for循环(或任何没有回调的循环)。
永远不要和forEach一起使用await,而是使用for循环(或任何没有回调的循环)。
不要在 filter 和 reduce 中使用 await,如果需要,先用 map 进一步骤处理,然后在使用 filter 和 reduce进行处理。