Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Trip into the async world @ NYC Kotlin Meetup
Search
Roberto Orgiu
October 16, 2019
Programming
0
91
Trip into the async world @ NYC Kotlin Meetup
These are the slides of my talk from NYC Kotlin Meetup @ Dropbox
Roberto Orgiu
October 16, 2019
Tweet
Share
More Decks by Roberto Orgiu
See All by Roberto Orgiu
Wellness & Droid
tiwiz
0
100
Behind the curtains
tiwiz
0
47
The Importance of Being Tested
tiwiz
0
390
An Android Dev start to Kotlin MPP
tiwiz
0
150
Fantastic API and where to find them
tiwiz
0
60
Flipping the Koin @ GDG Dev Party
tiwiz
1
48
Flipping the Koin
tiwiz
2
140
Trip into the async world
tiwiz
1
110
GraphQL IRL (Android Makers)
tiwiz
0
140
Other Decks in Programming
See All in Programming
『自分のデータだけ見せたい!』を叶える──Laravel × Casbin で複雑権限をスッキリ解きほぐす 25 分
akitotsukahara
2
650
ふつうの技術スタックでアート作品を作ってみる
akira888
1
1.1k
生成AI時代のコンポーネントライブラリの作り方
touyou
1
260
Railsアプリケーションと パフォーマンスチューニング ー 秒間5万リクエストの モバイルオーダーシステムを支える事例 ー Rubyセミナー 大阪
falcon8823
5
1.4k
Rubyでやりたい駆動開発 / Ruby driven development
chobishiba
1
750
新メンバーも今日から大活躍!SREが支えるスケールし続ける組織のオンボーディング
honmarkhunt
5
8.2k
テスト駆動Kaggle
isax1015
1
510
AI時代のソフトウェア開発を考える(2025/07版) / Agentic Software Engineering Findy 2025-07 Edition
twada
PRO
97
34k
Webの外へ飛び出せ NativePHPが切り拓くPHPの未来
takuyakatsusa
2
580
Hack Claude Code with Claude Code
choplin
6
2.4k
チームのテスト力を総合的に鍛えて品質、スピード、レジリエンスを共立させる/Testing approach that improves quality, speed, and resilience
goyoki
5
1k
イベントストーミング図からコードへの変換手順 / Procedure for Converting Event Storming Diagrams to Code
nrslib
2
970
Featured
See All Featured
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
18
980
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
35
2.4k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.7k
Adopting Sorbet at Scale
ufuk
77
9.5k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Testing 201, or: Great Expectations
jmmastey
43
7.6k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
7
330
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
181
54k
Practical Orchestrator
shlominoach
189
11k
Imperfection Machines: The Place of Print at Facebook
scottboms
267
13k
Documentation Writing (for coders)
carmenintech
72
4.9k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Transcript
Trip into the async world
Rob
Why RxJava?
Why RxJava? Retrofit
Why RxJava? Retrofit Async ops
Why RxJava? Retrofit Async ops Basic operators
Why RxJava? Retrofit Async ops Basic operators Advanced operators
Why RxJava? Retrofit Async ops Basic operators Advanced operators
Why RxJava? Retrofit Async ops Basic operators Advanced operators
implementation ‘com.squareup.retrofit2:retrofit:2.6.1’
@GET("/path/to/resource") fun networkRequest(): Single<Data> @GET("/path/to/resource") suspend fun networkRequest(): Data
Retrofit.Builder() ... .addCallAdapterFactory(CoroutineCallAdapterFactory()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build()
CoroutineCallAdapterFactory() RxJava2CallAdapterFactory.create()
@Test fun test() { runBlocking { val response = api.networkRequest()
assertThat(response)... } api.networkRequest() .test() .assertValue { } }
None
class DataViewModel()) : ViewModel() { private val subscriptions = CompositeDisposable()
val output = BehaviorSubject<LCE<Data>>.create() fun loadCurrentStatus() { disposable += store.get(BarCode.empty()) .startWith(LceLoading()) .onErrorReturn({e -> LceError(e)}) .map({data -> mapEmptyData(data)}) .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe(output) } override fun onCleared() { disposable.clear() } }
class DataViewModel()) : ViewModel() { private val subscriptions = CompositeDisposable()
val output = BehaviorSubject<LCE<Data>>.create() fun loadCurrentStatus() { disposable += store.get(BarCode.empty()) .startWith(LceLoading()) .onErrorReturn({e -> LceError(e)}) .map({data -> mapEmptyData(data)}) .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe(output) } override fun onCleared() { disposable.clear() } } Hidden knowledge
te val subscriptions = CompositeDisposable() utput = BehaviorSubject<LCE<Data>>.create() oadCurrentStatus() {
isposable += store.get(BarCode.empty()) .startWith(LceLoading()) .onErrorReturn({e -> LceError(e)}) .map({data -> mapEmptyData(data)}) .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe(output) ide fun onCleared() { isposable.clear() Learning curve
class DataViewModel()) : ViewModel() { val output = MutableLiveData<Data>() fun
loadCurrentStatus() { output.postValue(LceLoading()) viewModelScope.launch { try { val data = store.get(BarCode.empty()).await() if (data.isEmpty()) { output.postValue(LceError(EmptyResultSetException())) } else { output.postValue(LceSuccess(data)) } } catch (e: Exception) { output.postValue(LceError(e)) } } } }
class DataViewModel()) : ViewModel() { val output = MutableLiveData<Data>() fun
loadCurrentStatus() { output.postValue(LceLoading()) viewModelScope.launch { try { val data = store.get(BarCode.empty()).await() if (data.isEmpty()) { output.postValue(LceError(EmptyResultSetException())) } else { output.postValue(LceSuccess(data)) } } catch (e: Exception) { output.postValue(LceError(e)) } } } }
adCurrentStatus() { output.postValue(LceLoading()) viewModelScope.launch { try { val data =
store.get(BarCode.empty()).await() if (data.isEmpty()) { output.postValue(LceError(EmptyResultSetException())) } else { output.postValue(LceSuccess(data)) } } catch (e: Exception) { output.postValue(LceError(e)) } }
map flatmap filter first last
map flatmap filter first last forEach orEmpty mapNotNull
@NotNull public static <T> Observable<Response<T>> from(@NotNull final ApolloCall<T> call) {
checkNotNull(call, "call == null"); return Observable.create(new ObservableOnSubscribe<Response<T>>() { @Override public void subscribe(final ObservableEmitter<Response<T>> emitter) throws Exception { cancelOnObservableDisposed(emitter, call); call.enqueue(new ApolloCall.Callback<T>() { @Override public void onResponse(@NotNull Response<T> response) { if (!emitter.isDisposed()) { emitter.onNext(response); } } @Override public void onFailure(@NotNull ApolloException e) { Exceptions.throwIfFatal(e); if (!emitter.isDisposed()) { emitter.onError(e); } } @Override public void onStatusEvent(@NotNull ApolloCall.StatusEvent event) { if (event == ApolloCall.StatusEvent.COMPLETED && !emitter.isDisposed()) { emitter.onComplete(); } } }); } }); }
@NotNull public static <T> Observable<Response<T>> from(@NotNull final ApolloCall<T> call) {
checkNotNull(call, "call == null"); return Observable.create(new ObservableOnSubscribe<Response<T>>() { @Override public void subscribe(final ObservableEmitter<Response<T>> emitter) throws Exception { cancelOnObservableDisposed(emitter, call); call.enqueue(new ApolloCall.Callback<T>() { @Override public void onResponse(@NotNull Response<T> response) { if (!emitter.isDisposed()) { emitter.onNext(response); } } @Override public void onFailure(@NotNull ApolloException e) { Exceptions.throwIfFatal(e); if (!emitter.isDisposed()) { emitter.onError(e); } } @Override public void onStatusEvent(@NotNull ApolloCall.StatusEvent event) { if (event == ApolloCall.StatusEvent.COMPLETED && !emitter.isDisposed()) { emitter.onComplete(); } } }); } }); } suspend fun <T> ApolloCall<T>.await(): Response<T> = suspendCancellableCoroutine { cont -> enqueue(object : ApolloCall.Callback<T>() { override fun onFailure(e: ApolloException) { cont.resumeWithException(e) } override fun onResponse(response: Response<T>) { cont.resume(response) } }) cont.invokeOnCancellation { cancel() } }
suspend fun <T> ApolloCall<T>.await(): Response<T> = suspendCancellableCoroutine { cont ->
enqueue(object : ApolloCall.Callback<T>() { override fun onFailure(e: ApolloException) { cont.resumeWithException(e) } override fun onResponse(response: Response<T>) { cont.resume(response) } }) cont.invokeOnCancellation { cancel() } }
val job = Job() val scope = CoroutineScope(Dispatchers.IO + job)
fun doThings() { scope.launch { ... } } fun inTheEnd() { job.cancel() }
val job = Job() val scope = CoroutineScope(Dispatchers.IO + job)
fun doThings() { scope.launch { ... } } fun inTheEnd() { job.cancelChildren() scope.coroutineContext.cancelChildren() }
job.cancelChildren() scope.coroutineContext.cancelChildren() public fun Job.cancelChildren(cause: CancellationException? = null) { children.forEach
{ it.cancel(cause) } } public fun CoroutineContext.cancelChildren(cause: CancellationException? = null) { this[Job]?.children?.forEach { it.cancel(cause) } }
None
kotlinx-coroutines-rx2
class RxThing { fun doStuff() : Single<MyData> }
suspend fun doThings() { val my data = rxThing.doStuff().await() }
class CoroutineThing { suspend fun doStuff() : MyData }
fun doStuff() : Single<MyData> = rxSingle { coroutineThing.doStuff() }
fun doStuff() : Single<MyData> = rxSingle(RxCoroutineExceptionHandler) { coroutineThing.doStuff() }
None
open.nytimes.com @nytdev github.com/NYTimes developers.nytimes.com
QA &