await
操作符只能在异步函数中使用,当你在一个异步操作之前放置await
操作符,那么函数的执行流就会在此“等待”,直到异步结果可用时,才继续执行。比如,我们要异步读取文件内容,等内容完全读取,然后再将内容写入到另一个文件中;为此,我们可以定义一个readWrite
函数,在它的内部,它会逐步执行异步操作:
code/async-await/read-write-file.js
async function readWrite() {
const content = await readFile('./example.txt', 'utf-8')
const result = await writeFile('./example-copy.txt', content)
return result
}
由于readWrite
使用了async
关键字,因此可以在其函数体内使用await
操作符,我们首先等待文件内容的读取,读取完毕后再异步执行写入操作,最后我们返回了异步操作的处理结果。
现在,我们再来看另外一个多个异步任务串行执行的例子,以下是各项操作的执行顺序:
- 发起Get请求到公用API获取一些新闻信息
- 列出文本文件下的文件列表,并选取以
.txt
结尾的文件 - 读取选取的文件内容,并把请求到的新闻信息附加到文件内容上
- 把最终的文件内容写入到本地文件
final.txt
上
在下面的代码块中,你将会看到我们是如何使用await
操作符来完成每一步异步操作的,它会等待异步操作的执行,直到取到操作结果才继续执行下一步:
code/async-await/post-body-example/main.js
async function main() {
const response = await axios.get(
'https://jsonplaceholder.typicode.com/posts/1'
)
const postBody = response.data.body
const localFolderList = await readdir('.')
const textFiles = localFolderList.filter(onlyTextFiles)
const localFileContent = await readFile(textFiles[0], 'utf-8')
const finalResult = localFileContent + postBody
const writeResult = await writeFile('./final.txt', finalResult)
return writeResult
}
乍一看,上面的代码像是同步的,但实际上并不是。每个任务执行的时候,并没有阻塞任何东西。这一点非常重要,异步代码不会阻塞任何将要被执行的操作,这对执行效率大有帮助。现代,让我们深入分析上面的代码。问问自己:哪些任务是能够独立处理的?哪些任务是需要按照特定顺序执行的?换句话说,我们是否可以把操作分成两个或者多个异步部分?一个可能的做法就是把它切分两个操作:
- 读取文件夹的内容,获取文本文件,读取文本文件的内容
- 发起Get请求获取新闻信息
简单的分析,你会发现,发起Get请求和读取文件信息是没有先后顺序的。然而,要读取本地文本内容,你需要先读取文件夹内容,并过滤出文本文件。现在,我们重新组织了需要完成的任务,我们可以使用Promise.all
来并发执行任务:
code/async-await/post-body-example/main-group.js
async function readLocalContent() {
const localFolderList = await readdir('.')
const textFiles = localFolderList.filter(onlyTextFiles)
const localFileContent = await readFile(textFiles[0], 'utf-8')
return localFileContent
}
async function getPostObject() {
const response = await axios.get(
'https://jsonplaceholder.typicode.com/posts/1'
)
return response.data.body
}
async function main() {
try {
const results = await Promise.all([readLocalContent(), getPostObject()])
const finalContent = results[0] + results[1]
return writeFile('./final2.txt', finalContent)
} catch (e) {
return Promise.reject(e)
}
}
在上面的代码中,我们把任务切分成两个异步函数来处理,这样我们就可以并发地执行它们。在main
函数中,我们使用Promise.all
来等待它们全部执行,然后把它们的结果写入到本地文件中。