调用有 await
标记的异步函数在同一时间只能执行一段代码。在异步代码执行的过程中,调用方需要等待异步代码执行完后才能继续执行下一行代码。比如,当你想从图库中拉取前三张图片,可以像下面这样,等三次调用完后再执行 downloadPhoto(named:)
函数:
let firstPhoto = await downloadPhoto(named: photoNames[0])
let secondPhoto = await downloadPhoto(named: photoNames[1])
let thirdPhoto = await downloadPhoto(named: photoNames[2])
let photos = [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
这种方式有一个非常严重的缺陷:虽然下载过程是异步的,并且在等待过程中可以执行其他任务,但每次只能执行一个 downloadPhoto(named:)
。每一张图片只能在上一张图片下载结束了才开始下载。然而,并没有必要让这些操作等待,每张图片可以独立甚至同时下载。
为了在调用异步函数的时候让它附近的代码并发执行,定义一个常量时,在 let
前添加 async
关键字,然后在每次使用这个常量时添加 await
标记。
async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])
let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)
在上面的例子中,三次调用 downloadPhoto(named:)
都不需要等待前一次调用结束。如果系统有足够的资源,这三次调用甚至都可以同时执行。这三次调用都没有没标记为 await
,因为代码不需要被挂起等待函数的结果。程序会继续执行直到 photos
被定义,与上面不同的是,在这个时间点由于程序需要上面几次异步调用的结果,所以你需要添加 await
关键字来挂起当前代码的执行直到所有图片下载完成。
下面是关于两种不同方法的一些说法:
- 代码中接下来的几行需要依赖异步函数的结果时,需要使用
await
来调用异步函数。这样产生的结果是有序的。 - 短时间内并不需要异步函数的结果时,需要使用
async-let
来调用异步函数。这样产生的任务是并发的。 await
和async-let
都允许其他任务在他们被挂起的时候执行。- 在两种情况下,都需要用
await
标记可能的悬点,以表明代码在这些点在需要的情况下会被挂起,直到异步函数执行结束。
你也可以在同一段代码中混用两种方式。