Swift async-attesa vs chiusure
Swift awaitfunziona catturando il contesto e sospendendo l'esecuzione fino al asyncritorno del metodo chiamato. Un'altra cosa che funziona in modo simile catturando il contesto circostante è una chiusura sfuggente. Quindi async-awaitle chiamate possono essere immaginate come equivalenti a sfuggire alla chiusura. Ogni volta che vedi un metodo con asyncsostituisci mentalmente quel metodo con un gestore di completamento di escape.
func run() async
func run(_ completion: @escaping () -> Void)
Uno scenario semplice con codice equivalente
class Foo {
func before() { /*...*/ }
func after() { /*...*/ }
func doStuff() async { /*...*/ }
func doStuff(_ completion: @escaping () -> Void) { /*...*/ }
}
extension Foo {
func run() async {
before()
await doStuff()
after()
}
}
extension Foo {
func run(_ completion: @escaping () -> Void) {
before()
doStuff {
self.after()
completion()
}
}
}
func doStuff() async -> Int
let ret = await doStuff()
print("\(ret)")
func doStuff(_ completion: @escaping (Int) -> Void)
doStuff { ret in
print("\(ret)")
}
func div(_ a: Int, _ b: Int) async throws -> Double {
if b == 0 {
throw FooError.divideByZero
}
return Double(a) / Double(b)
}
do {
let ret = try await div(1, 0)
print("\(ret)")
} catch {
print("\(error)")
}
func div(_ a: Int, _ b: Int, _ completion: @escaping (Double) -> Void) throws {
if b == 0 {
throw FooError.divideByZero
}
completion(Double(a) / Double(b))
}
do {
try div(1, 0) { ret in
print("\(ret)")
}
} catch {
print("\(error)")
}
Nota come la asyncchiamata si propaga naturalmente al chiamato. Quindi anche una chiamata a doStuff() asyncfa run() async. Nella maggior parte dei casi, questo ha senso anche per coloro che effettuano il completamento. E se desideriamo non propagare il comportamento asincrono, o in altre parole, desideriamo convertire un asyncmetodo in un syncmetodo, dobbiamo usare a Taskche accetta un gestore di completamento ed è equivalente al wrapping all'internoDispatchQueue.async { ... }
func run() {
before()
Task {
await doStuff()
after()
}
}
func run() {
before()
DispatchQueue.global().async {
self.doStuff {
self.after()
}
}
}
Se desideriamo chiamare i metodi asincroni uno dopo l'altro, il modello mentale rimane lo stesso
func doStuff() async {}
func doMoreStuff() async {}
before()
await doStuff()
await doMoreStuff()
after()
func doStuff(_ completion: @escaping () -> Void) {}
func doMoreStuff(_ completion: @escaping () -> Void) {}
before()
doStuff {
self.doMoreStuff {
self.after()
}
}
func run() async {
before()
async let task1: Void = doStuff()
async let task2: Void = doMoreStuff()
_ = await [task1, task2]
after()
}
func run(_ completion: @escaping () -> Void) {
before()
let group = DispatchGroup()
group.enter()
doStuff { group.leave() }
group.enter()
doMoreStuff { group.leave() }
group.notify(queue: DispatchQueue.global()) {
self.after()
completion()
}
}
Avere un modello mentale migliore per l'attesa asincrona aiuta ad apprezzare il tipo di insidie da cui ci salva e anche come migrare dai gestori di completamento all'attesa asincrona.

![Che cos'è un elenco collegato, comunque? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































