以前投稿した内容で
Combine.frameworkのFuture
はインスタンスを生成した時点で
中のクロージャを実行させるということについて書きました。
https://qiita.com/shiz/items/f089c93bdebfaef2196f
https://developer.apple.com/documentation/combine/future
この場合
Subscribeをしていないのに処理が実行されてしまい
無駄にリソースを消費してしまう可能性や
副作用を起こして思わぬ動作をしてしまう可能性もあります。
そういう場合Deferred
を活用する方法があります。
Deferred
はinitでcreatePublisher
というクロージャを受け取り
中でPublisherを生成します。
このクロージャはSubscribeした時に初めて実行されます。
検証
下記のコードをPlaygroundで実行して確認します。
まずFutureの場合
let futurePublisher = Future<String, Never> { promise in
print("Future 実行")
promise(.success("hello"))
}
この時点で
Future 実行
が出力されます。
一方Deferred
の場合
let deferredPublisher = Deferred { () -> Just<String> in
print("Deferred 実行")
return Just("hello")
}
これだけ書いても何も実行されません。
Subscribeしてみると
let cancellable = deferredPublisher
.sink(receiveCompletion: {
print("receiveCompletion \($0)")
}, receiveValue: {
print("receiveValue \($0)")
})
Deferred 実行
receiveValue hello
receiveCompletion finished
とSubscribeしてから
クロージャの中が実行されていることがわかります。
これを活用してFuture
を遅延実行させる方法が
下記の動画でも紹介されています。
※
下記の例はFuture
の特徴をよく表しているため引用させていただきました。
注意点として下記の例にある2番目の1729という値を受け取ることはありません。
Futureは値を一度受け取るとすぐにCompletionするため
受け取れるのは最初の42だけです。
動画では値を継続的に受け取りたいため
この後Custom Publisherの生成を行っています。
let aFutureInt = Deferred {
Future<Int, Never> { callback in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
print("Hello from inside the future!")
callback(.success(42))
callback(.success(1729))
}
}
}
※ この時点ではまだSubscribeしていないため
実行はされません。
まとめ
Deferred
について検証してみました。
Deferred
を活用することで不必要な処理やセットアップなどが
実行されるリスクを減らすことができます。
またFuture
を一緒に活用することで
非同期処理の遅延実行も実現することができることもわかりました。
しかし
Future
は値を一度して受け取らないという特徴を持っているため
使い方を知らないと
「あれっ?なんか思っていたのと違う」
となってしまう可能性もあるので注意が必要ですね😅
何か間違いなどございましたら教えていただけますとうれしいです🙇🏻♂️