Swift async-await 대 클로저

Dec 07 2022
Swift await는 호출된 asyncmethod가 반환될 때까지 컨텍스트를 캡처하고 실행을 일시 중단하여 작동합니다. 주변 컨텍스트를 캡처하여 유사하게 작동하는 또 다른 기능은 이스케이프 클로저입니다.

Swift 는 컨텍스트를 캡처하고 호출된 메서드가 반환 await될 때까지 실행을 일시 중단하여 작동 합니다. async주변 컨텍스트를 캡처하여 유사하게 작동하는 또 다른 기능은 이스케이프 클로저입니다. 따라서 async-await호출은 이스케이프 클로저와 동일하다고 상상할 수 있습니다. 메서드가 있는 메서드를 볼 때마다 async정신적으로 해당 메서드를 이스케이프 완료 처리기로 대체합니다.

func run() async
func run(_ completion: @escaping () -> Void)

동등한 코드가 있는 간단한 시나리오

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)")
}

async호출이 수신자에게 자연스럽게 전파되는 방식에 주목하십시오 . doStuff() async따라서 make에 대한 호출 run() async도 마찬가지입니다. 이는 대부분의 경우 완료 핸들러에 대해서도 의미가 있습니다. 그리고 비동기 동작을 전파하지 않으려면, 즉 async메서드를 메서드로 변환하려면 완료 핸들러를 받는 synca를 사용해야 하며 Task내부에서 래핑하는 것과 동일합니다.DispatchQueue.async { ... }

func run() {
  before()
  Task {
    await doStuff()
    after()
  }
}
func run() {
  before()
  DispatchQueue.global().async {
    self.doStuff {
      self.after()
    }
  }
}

비동기 메서드를 차례로 호출하려는 경우 정신 모델은 동일하게 유지됩니다.

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()
  }
}

async-await에 대한 더 나은 멘탈 모델을 갖는 것은 그것이 우리를 어떤 종류의 함정으로부터 구하는지, 그리고 완료 핸들러에서 async await로 마이그레이션하는 방법을 이해하는 데 도움이 됩니다.

추가 자료