Async / Await

What is Async/Await?

swift5.5에 등장한 async/await는 이미 Javascript, kotlin등의 다른 언어에서 제공되던 비동기 처리방식을 편하게 해주는 문법이다
그동안 swift에서는 비동기 프로그래밍을 delegate pattern, DispatchQueue, Closure등으로 처리했지만 async/await를 사용하면 더욱 편하고 짧은 코드로 비동기 처리방식을 구현할수 있다

// DispatchQueue 사용한 비동기 처리
DispatchQueue.global.async {

}

// Closure를 사용한 비동기 처리
let task = URLSession.shared.dataTask(with: url) { data, response, error in

}.resume()
  • 이외에도 RxSwift나 Combine같은 대표적인 비동기 처리방법이 있다

async

async마크가 표기된 함수는 비동기로 동작하는 함수가 된다

func someWork() async throws -> String {
  ...
}

위 예제처럼 async 마크 위치는 함수 매개변수 선언 뒤이고 throws의 앞이다.
여기서 throws는 반드시 필요한 것이 아니라 그저 위치 설명을 위해서 추가한 것이다.

또한 async는 프로퍼티에도 마크하는 것이 가능하다

struct SomeStruct {
  var value: String {
    get async throws {
      ...
    }
  }
}

동시에 프로퍼티에도 throws를 표기할 수 있게 된다

await

await 는 비동기 함수의 실행을 동기하는 명령이다. 즉 현재 스레드에서 해당 비동기 함수의 실행이 끝날 때까지 대기한다는 말이다.

func doWhatWithSomeWork() async {
  ...
  try? await someWork()
  ...
}

await 는 블로킹(blocking)을 유발하므로 async 로 마크된 함수 등 비동기 컨텍스트 내에서만 쓸 수 있다. 당연하게도 await 중인 함수의 실행이 완료될 때까지 코드의 실행은 중간에 멈추게(blocking) 된다.

await 를 사용할 수 있는 곳은 여러 곳이 있는데 위치가 미묘하긴 하다.
예를 들어 for 루프의 경우 공식 홈페이지 소개 내용에 아래와 같은 식의 예제를 소개하고 있다.

import Foundation

let handle = FileHandle.standardInput
for try await line in handle.bytes.lines {
    print(line)
}

How to use?

  • 메소드 시그니처에 async를 붙여 비동기 작업임을 알리는 키워드를 추가
  • 메소드 내부 구현에서 비동기가 예상되는 곳에 await 키워드를 추가
  • 해당 메소드를 호출하는 쪽에서 Task키워드를 붙여 async block을 호출함

example code

@objc func didTapButton() {
  guard let baseURL = URL(string: "https://example/image.png") else { return }

  Task {
    guard let imageURL = try? await self.requestImageURL(requestURL: baseURL),
          let url = URL(string: imageURL),
          let data = try? Data(contentsOf: url)
          else { return }
  
    print(Thread.isMainThread) // true
    self.imageView.image = UIImage(data: data)
  }
}

func requestImageURL(requestURL: URL) async throws -> String {
  print(Thread.isMainThread) // false
  let (data, _) = try await URLSession.shard.data(from: requestURL)
  return try JSONDecoder().decode(MyModel.self, from: data).data.first?.profile ?? ""
}

References

[iOS - swift] Async, Await 사용 방법
Swift async/await