async/await遇到map和reduce
map
首先我们来看看同步的map怎么写。
// 对数组所有元素乘2
[1,2,3].map(value => value * 2); // [2,4,6]
复制代码
那如果map函数需要进行异步操作才能返回结果应该怎么写呢?
[1, 2, 3].map(async value => value * 2); // [Promise, Promise, Promise]
复制代码
async函数执行完会返回Promise对象,map就直接接收后装进新数组了,数组内容直接变成了三个Promise,这显然不是我们想要的结果,所以我们要对Promise数组再进一步操作取出其中的值。
await Promise.all([1, 2, 3].map(async value => value * 2)) // [2,4,6]
复制代码
这里的Promise.all会将一个由Promise组成的数组依次执行,并返回一个Promise对象,该对象的结果为数组产生的结果集。
reduce
对于reduce来说,也是基本和map差不多的思路,只是需要提前将前一次的结果用await取出Prmose的值,再进行运算。
await [1, 2, 3].reduce(async(previousValue, currentValue) => await previousValue + currentValue, 0) // 6
复制代码
filter
感觉好简单啊,那数组的异步filter能不能也像map这么写呢?
await Promise.all([1, 2, 3].filter(async value => value % 2 === 1)) // [1,2,3]
复制代码
结果没对啊,async返回的Promise被直接判断成true,导致一个元素也没被过滤掉。
const filterResults = await Promise.all([1, 2, 3]
.map(async value => (value % 2 === 1))); // [true,false,true]
[1, 2, 3].filter((value, index) => filterResults[index]); // [1,3]
复制代码
这里我们使用了一个临时数组配合前面map先获取异步filter对应每个元素的结果,然后再使用filter过滤数组,搞定~
Promise库
刚开写asyncMap的时候,以为其他的方法也会这么简单,后来发现事情并没简单,只好找了个Promise库 bluebird,专门处理这些异步操作~~~
除了提供有常见的map、filter、reduce、some之外,还提供了PromisifyAll,直接把回调库Promise化。
var fs = require("fs");
Promise.promisifyAll(fs);
fs.readFileAsync("file.js", "utf8").then(...)
复制代码
也支持第三方库
var Promise = require("bluebird");
Promise.promisifyAll(require("redis"));
自测
附上一个async/await结合reduce的自测题
题目
// 要求实现asyncWrap函数,输出1+2+3的值
async function asyncAdd(a,b){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve(a+b)
},500)
})
}
asyncWrap(asyncAdd)([1,2,3]).then(data=>{
console.log(data)
})
// 思路
asyncWrap()
asyncWrap(asyncAdd)([1,2,3])
asyncWrap(asyncAdd)
reduce
答案
async function asyncAdd(a, b) {
return new Promise(resolve => {
setTimeout(() => {
resolve(a + b)
}, 500)
})
}
function asyncWrap(fn) {
return async arr => {
return await arr.reduce(async (acc, value) => {
let repromise = await acc
return await fn(repromise, value)
})
}
}
asyncWrap(asyncAdd)([1, 2, 3]).then(data => {
console.log(data)
})