study record

[Swift Combine] Future, Deferred 본문

Swift/Combine

[Swift Combine] Future, Deferred

asong 2024. 3. 31. 21:36

Future

하나의 값을 만들고 끝나거나 실패하는 publisher

final class Future<Output, Failure> where Failure : Error

 

어떤 작업을 수행하고 비동기적으로 하나의 값을 publish 하고자 할 때 future를 사용한다. 첫번째 value를 보내면 스트림이 바로 끝난다.

Future.Promise를 받는 클로저로 future를 initialize한다. 성공 또는 실패를 가리키는 Result와 함께 promise를 호출한다.

결과가 error인 경우 publishing은 에러와 함께 종료된다.

 

func generateAsyncRandomNumberFromFuture() -> Future <Int, Never> {
    return Future() { promise in
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            let number = Int.random(in: 1...10)
            promise(Result.success(number))
        }
    }
}
cancellable = generateAsyncRandomNumberFromFuture()
    .sink { number in print("Got random number \\(number).") }
 
 
// 두번째 값은 출력이 되지 않는다.   
let future = Future<Int, Never> { promise in
    promise(.success(1))
    promise(.success(2))
}

future.sink(receiveCompletion: { print($0) },
            receiveValue: { print($0) })
            
            
// 출력
// 1
// finished

 

Integrating with Swift Concurrency

Future는 오직 하나의 요소만 방출하므로 특히 유용하다.

value property를 사용함으로써 쉽게 async await 으로 사용할 수 있다.

let number = await generateAsyncRandomNumberFromFuture().valueprint("Got random number \\(number).")

 

[주의]

Future Publisher의 경우, 구독자가 없어도 값을 방출한다.

따라서 구독자가 있을 때 값을 방출하도록 하고싶다면 Deferred를 활용하여 구독 후 값을 하나 방출하게끔 해야 한다!

 

Empty

값을 게시하지 않고 즉시 완료되는 Publisher 이다.

Empty는 아무런 값도 내보내지 않고 즉시 completion 이벤트를 보낼지 선택할 수 있는 Publisher이다.

Empty<String, Never>()
      .sink(
          receiveCompletion: {
              print($0) // finished
          },
          receiveValue: {
              print("receiveValue : \\($0)") // 출력 안함
          }
      )
      
let anyPublisher = [1, nil, 3].publisher
    .flatMap { value -> AnyPublisher<Int, Never> in
        if let value = value {
            return Just(value).eraseToAnyPublisher()
        } else {
            return Empty().eraseToAnyPublisher()
        }
    }.eraseToAnyPublisher()

anyPublisher.sink(receiveCompletion: { print("AnyPublisher completion: \\($0)") },
                  receiveValue: { print("value: \\($0)") }
)

 

Deferred

새 subscriber를 위한 publisher를 만드는 클로저를 제공하기 전에는 subscription을 기다리는 Publisher

struct Deferred<DeferredPublisher> where DeferredPublisher : Publisher

 

deferred를 사용하면 구독 시점에 따라 값을 다르게 받을 수 있다. 구독을 해야지만 값이 방출되므로 구독 시점에 영향을 받는다.

Deferred { Just(Void()) }
  .sink(receiveValue: { print("Diferred") })

 

 

 

 

→ 아쉬운 점

combine → concurrency로 옮기면 더 좋다! (더 최신식.. 더 간결)

 

 

 

참고

'Swift > Combine' 카테고리의 다른 글

[Combine] 1. Hello Combine!  (1) 2024.08.28
[Swift] Combine이란?  (0) 2023.01.03