ng-japan meetup 2017 Autumn (2017年10月10日)
LT発表資料
名前: ちきさん
所属: オプト
GitHub/Twitter/Qiita: @ovrmrw
We are hiring!
JSBin
StackBlitz
よくありそうなユーザーストーリー
引数valueを受けて、非同期処理をした後に処理結果をObservable型で返す関数があったとして、
function httpRequest$(value) {
const promise = new Promise(resolve => {
setTimeout(() => {
resolve(value + '!') // valueに "!" を付けて返す
}, Math.random() * 200) // timeoutはランダムで変わる
})
return Observable.from(promise)
}
subscribeしてその処理結果を使って何かしたいとする。
function log(result) {
console.log(result) // ここではコンソールに出力しているだけ
}
例えばキーワードが入力される度にHTTPリクエストが走る入力フォームがあったとして、
httpRequest$('a').subscribe(log)
httpRequest$('ab').subscribe(log)
httpRequest$('abc').subscribe(log)
レスポンスは毎回同じ順番で来るとは限らないので、
"ab!"
"a!"
"abc!"
こうなったり
"abc!"
"ab!"
"a!"
こうなったりする。
でもこのときに欲しい結果は "abc!"
だけだったりします。
今回はそういうケースでどうすればさくっと解決できるのか、というお話。
Subject と switchMap
前述のユーザーストーリーでは、要するに最後のリクエストより前のリクエスト(非同期処理)はキャンセルしてしまえば良いので、
まずSubjectを用意する。
const dispatcher$ = new Subject()
const provider$ = new Subject()
それぞれの役目
-
dispatcher
... ストリームに引数valueを流す -
provider
... 別のストリームに処理後の値を流す
dispatcher
から始まるストリームではswitchMap
で後続の非同期処理をキャンセルしつつ、処理結果をprovider
のストリームに流す。
// Serviceのconstructorとかで
Observable
.from(dispatcher$)
.switchMap(value => {
return httpRequest$(value) // 非同期処理をswitchMapの中に持ってくる
})
.subscribe(result => {
provider$.next(result)
})
そしてprovider
をsubscribe
して何らかの処理をする。
// Serviceのどこかで
provider$
.subscribe(log)
先ほどは↓こうだったリクエストの発行が、
httpRequest$('a').subscribe(log)
httpRequest$('ab').subscribe(log)
httpRequest$('abc').subscribe(log)
↓このように変わるだけ。
dispatcher$.next('A')
dispatcher$.next('AB')
dispatcher$.next('ABC')
先ほどは↓こうだったレスポンスが、
"abc!"
"a!"
"ab!"
↓このように変わる。
"ABC!"
("A!"
と"AB!"
は非同期処理がキャンセルされたので出力もされない)
そんなに手間をかけずに非同期処理キャンセルの仕組みが作れました。
カンタンだ
JSBinで実際の動きを確認できます。 https://jsbin.com/vevukev/3/edit?js,console
StackBlitzで実際に組み込んだAngularアプリを編集できます。 https://stackblitz.com/edit/angular-wjrath