Swift Concurrency: Continuation

틀틀보·2025년 4월 30일

Swift Concurency

목록 보기
4/10

기존의 비동기 코드(콜백, 델리게이트 등등)을 async/await 패턴으로 변환할 때 사용하는 핵심 개념

Continuation 객체

비동기 함수의 실행 상태를 저장하고, 중단된 지점에서 재개 할 수 있도록 하는 객체

사용 예시

func fetchData(completion: @escaping (Result<String, Error>) -> Void) {
    // 비동기 작업 수행 후 completion 호출
    completion(.success("데이터"))
}

func fetchDataAsync() async throws -> String {
    return try await withCheckedThrowingContinuation { continuation in
        fetchData { result in
            switch result {
            case .success(let data):
                continuation.resume(returning: data)
            case .failure(let error):
                continuation.resume(throwing: error)
            }
        }
    }
}

기존의 콜백 기반 함수를 aync/await 스타일로 쉽게 변환 가능.

장점

  • 기존 코드와의 호환성: 기존 코드에 쉽게 적용 가능
  • 성능 향상: 스레드 전환 없이 함수 호출 수준의 비용으로 비동기 작업 처리 가능

유의할 점

  • 반드시 resume 메서드는 한 번만 호출되어야 하며, 아니면 크래시 발생
  • resume 메서드를 호출하지 않을 경우, 함수는 무한 대기 상태에 빠짐.

withCheckedContinuation, withCheckedThrowingContinuation

런타임에서 resume중복 호출되는지 확인 (런타임 에러) 및 async/await 스타일로 변환해주는 함수

// 기존 콜백 기반 함수
func loadData(completion: @escaping (Result<String, Error>) -> Void) {
    // 비동기 작업
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
        completion(.success("Loaded Data"))
    }
}

// async/await로 변환
func loadDataAsync() async throws -> String {
    try await withCheckedThrowingContinuation { continuation in
        loadData { result in
            switch result {
            case .success(let data):
                continuation.resume(returning: data)
            case .failure(let error):
                continuation.resume(throwing: error)
            }
        }
    }
    /*
    func myFunction() async -> String {
    return await withCheckedContinuation { continuation in
        // 비동기 작업 수행
        // 완료 시:
        continuation.resume(returning: "결과값")
    	}
	}
    */
}
  • withCheckedThrowingContinuation: Error 던짐
    성공 시: resume(returning:)
    실패 시: resume(throwing:)

  • withCheckedContinuation: Error 안 던짐

  • 런타임에 resume중복, 누락 체크 O

  • 런타임에 체크하므로, 약간의 오버헤드 발생

  • 안정성 향상

withUnsafeContinuation, withUnsafeThrowingContinuation

런타임에서 resume중복 호출 확인Xasync/await 스타일로 변환해주는 함수

func fetchData(url: URL) async -> Data? {
    await withUnsafeContinuation { continuation in
        URLSession.shared.dataTask(with: url) { data, _, _ in
            continuation.resume(returning: data)
        }.resume()
    }
}

func loadAsync() async throws -> String {
    try await withUnsafeThrowingContinuation { continuation in
        someAsyncFunction { result, error in
            if let result = result {
                continuation.resume(returning: result)
            } else if let error = error {
                continuation.resume(throwing: error)
            }
        }
    }
}
  • 런타임에 resume중복, 누락 체크 X
  • 런타임에 체크를 하지 않으므로, 약간의 성능 향상
  • 성능이 극도로 중요한 상황에서 사용 (최적화)

https://github.com/swiftlang/swift-evolution/blob/main/proposals/0300-continuation.md

profile
안녕하세요! iOS 개발자입니다!

0개의 댓글