假如你需要对多个文件进行操作,最直接的做法就是使用类似forEach
方法:
const files = ['./a.txt', './b.txt', './c.txt']
files.forEach(file => {
const r1 = await task1(file)
const r2 = await task2(r1)
})
上面的代码存在两个问题:
- 传入
forEach
方法中的函数没有使用async
关键字,所以我们不能在其内部使用await
关键字。 - 即使我们使用
async
关键字来定义传入到forEach
方法中的函数,forEach
方法也不会等待所有的异步任务都完成。
与forEach
方法相反,其他两个循环可行的:
- C语言风格的
for
循环
async function main() {
const files = ['./a.txt', './b.txt', './c.txt']
for (let i = 0; i < files.length; i++) {
const r1 = await task1(files[i])
const r2 = await task2(r1)
}
return 'done'
}
for of
循环
async function main() {
const files = ['./a.txt', './b.txt', './c.txt']
for (const file of files) {
const r1 = await task1(file)
const r2 = await task2(r1)
}
return 'done'
}
上面两种方式的弊端是,我们无法并行的执行任务,尽管它们都是异步任务。例如,如果第一个文件比较大,最后一个文件比较小,main
函数仍然会操作第一个文件完毕之后才会处理其他任务。更高效的做法是,我们把每个任务都定义为promise
,然后使用promise.all
来处理它们:
const files = ['./a.txt', './b.txt', './c.txt']
async function operation(f) {
const r1 = await task1(f)
const r2 = await task2(r1)
return r2
}
const tasksPromises = files.map(operation)
Promise.all(tasksPromises)
.then(r => console.log(r))
.catch(e => console.log(e))
.then(_ => console.log('all done'))
上面的代码中,我们使用一个异步函数来定义两个串行执行的异步任务,然后使用了map
方法生成了由异步函数组成的数组,最后,使用promise.all
来并发处理这些异步任务。注意,数组中的异步任务的处理不是按照特定顺序执行的,但是operation
函数中的两个异步任务是按照我们想要的顺序执行的。