swift5.5中加入了async/await
我写了一段这样的代码:
DispatchQueue.global().async { [weak self] in
self?.test()
}
}
func test() async {
let m = await cakkBackData4()
print(m)
}
然后提醒我如下的错误:
‘async’ call in a function that does not support concurrency
确切说就是:await cakkBackData4()的方法必须在async函数中进行调用。即调用者必须是异步的。
如何解决:
添加task 会立马去执行
以下说明如何使用
异步函数或异步方法是一种特殊的函数或方法,可以在执行过程中暂停。这与普通的同步函数和方法不同,后者要么运行到完成,要么抛出错误,要么永不返回。 异步函数或方法仍然做这三件事中的一件,但它也可以在等待的时候在中间暂停。在异步函数或方法的主体中,标记每个可以暂停执行的位置。
func listPhotos(inGallery name: String) async -> [String] {
let result =
return result
}
调用异步方法时,执行将暂停,直到该方法返回。你在电话前写下await标记可能的暂停点。这就像在调用抛出函数时编写try,以便在出现错误时标记程序流的可能更改。在一个异步方法中,只有当调用另一个异步方法时,执行流才会暂停。暂停从不隐式或抢占式,这意味着每个可能的暂停点都标记为await。
示例如下:
let photoNames = await listPhotos(inGallery: "Summer Vacation")
let sortedNames = photoNames.sorted()
let name = sortedNames[0]
let photo = await downloadPhoto(named: name)
show(photo)
由于listPhotos(inGallery:)和downloadPhoto(Name:)函数都需要发出网络请求,因此它们可能需要相对较长的时间才能完成。通过在返回箭头之前写入async,使它们都异步,这样应用程序的其余代码就可以在等待图片准备就绪时继续运行。
为了理解上述示例的并发性,这里有一个可能的执行顺序:
- 代码从第一行开始运行,一直运行到第一个await。它调用listPhotos(inGallery:)函数,并在等待该函数返回时暂停执行。
- 当此代码的执行被挂起时,同一程序中的其他一些并发代码会运行。例如,一个长期运行的后台任务可能会继续更新新照片库的列表。该代码也会一直运行到下一个挂起点(由wait标记)或完成。
- listPhotos(inGallery:)返回后,此代码将从该点开始继续执行。它指定返回给photoNames的值。
- 定义sortedNames和name的行是常规的同步代码。因为这些线路上没有标记等待,所以没有任何可能的暂停点。
- 下一个wait标记对downloadPhoto(名为:)函数的调用。这段代码再次暂停执行,直到该函数返回,给其他并发代码一个运行的机会。
- downloadPhoto(名为:)返回后,其返回值被分配给photo,然后在调用show(:)时作为参数传递。
代码中标记为wait的可能挂起点表明当前代码段可能会在等待异步函数或方法返回时暂停执行。 这也称为产生线程,因为在幕后,Swift会暂停当前线程上的代码执行,并在该线程上运行其他代码。因为带有wait的代码需要能够暂停执行,所以只有程序中的某些地方可以调用异步函数或方法:
任务完成了。sleep(纳秒:)方法在编写简单代码以了解并发如何工作时非常有用。此方法不执行任何操作,但在返回之前至少等待给定的纳秒数。下面是listPhotos(inGallery:)函数的一个版本,它使用睡眠(纳秒:)来模拟等待网络操作:
func listPhotos(inGallery name: String) async throws -> [String] {
try await Task.sleep(nanoseconds: 2 * 1_000_000_000)
return ["IMG001", "IMG99", "IMG0404"]
}
上一节中的listPhotos(inGallery:)函数在数组的所有元素就绪后立即异步返回整个数组。另一种方法是使用异步序列一次等待集合的一个元素。下面是在异步序列上迭代的情况:
import Foundation
let handle = FileHandle.standardInput
for try await line in handle.bytes.lines {
print(line)
}
上面的例子没有使用普通for in循环,而是在for之后使用wait编写for。与调用异步函数或方法一样,写入wait表示可能的挂起点。for await in循环可能会在每次迭代开始时暂停执行,此时它正在等待下一个元素可用。
用wait调用异步函数一次只运行一段代码。当异步代码运行时,调用方等待该代码完成,然后继续运行下一行代码。例如,要从图库中获取前三张照片,您可以等待对downloadPhoto(名为:)函数的三次调用,如下所示:
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(名为:)的调用。在下一张照片开始下载之前,每张照片都会被完全下载。然而,这些操作不需要等待每张照片可以独立下载,甚至可以同时下载。
要调用异步函数并使其与周围的代码并行运行,请在定义常数时在let前面写async,然后每次使用该常数时写wait。
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)
func test() async {
async let m1 = cakkBackData4(1)
async let m2 = cakkBackData4(2)
async let m3 = cakkBackData4(3)
async let m4 = cakkBackData4(7)
let m = await m1 + m2 + m3 + m4
print(m)
}
func cakkBackData4(_ n: Int) async -> Int {
do {
try await Task.sleep(nanoseconds: UInt64(n * 1_000_000_000))
} catch {
}
print(Thread.current)
return 10
}
在本例中,对downloadPhoto(名为:)的所有三个调用都会在不等待前一个调用完成的情况下启动。如果有足够的可用系统资源,它们可以同时运行。 这些函数调用都没有标记为wait,因为代码不会挂起以等待函数的结果。相反,执行会一直持续到定义照片的那一行,此时程序需要这些异步调用的结果,因此您需要等待写入以暂停执行,直到所有三张照片都完成下载。
以下是您如何看待这两种方法之间的差异:
- 当下面几行的代码取决于该函数的结果时,使用wait调用异步函数。这将创建按顺序执行的工作。
- 在代码后面不需要结果时,使用async let调用异步函数。这就产生了可以并行进行的工作。
- wait和async都允许其他代码在挂起时运行。
- 在这两种情况下,都用wait标记可能的暂停点,以指示执行将暂停(如果需要),直到异步函数返回。
任务和任务组
task是可以作为程序的一部分异步运行的工作单元。所有异步代码都作为某个任务的一部分运行。上一节中描述的async-let 语法为您创建了一个子任务。您还可以创建一个任务组,并将子任务添加到该组中,这样可以更好地控制优先级和取消,并允许您创建动态数量的任务。
任务按层次结构排列。任务组中的每个任务都有相同的父任务,每个任务都可以有子任务。由于任务和任务组之间的明确关系,这种方法被称为结构化并发。尽管您承担了一些正确性方面的责任,但任务之间明确的父子关系允许Swift处理一些行为,比如传播取消,并允许Swift在编译时检测一些错误。
此处待补充~
|