點燈坊

失くすものさえない今が強くなるチャンスよ

使用 concat() 依照 Argument 順序合併 Observable

Sam Xiao's Avatar 2020-07-18

實務上常會將眾多 Observable 加以合併,concat() 除了會將 Observable 合併外,還會依照 Argument 順序合併,直到一個 Observable 完成,才會繼續合併下一個 Observable。

Version

macOS Catalina 10.15.5
WebStorm 2020.1.2
Vue 2.6.11
RxJS 6.5.5

concat()

concat

按下 Show Message 後,會陸續顯示 Get Ready!321Go!

<template>
  <div>
    <button v-stream:click="click$">Show Message</button>
    <div>{{ message$ }}</div>
  </div>
</template>

<script>
import { concat, EMPTY } from 'rxjs'
import { startWith, delay, switchMapTo } from 'rxjs/operators'

let delayMsg$ = x => EMPTY.pipe(
  startWith(x),
  delay(1000)
)

let subscriptions = function() {
  let slogan$ = concat(
    delayMsg$('Get Ready!'),
    delayMsg$(3),
    delayMsg$(2),
    delayMsg$(1),
    delayMsg$('Go!')
  )

  let message$ = this.click$.pipe(
    switchMapTo(slogan$)
  )

  return { message$ }
}

export default {
  name:'App',
  domStreams: [
    'click$'
  ],
  subscriptions
}
</script>

12 行

let delayMsg$ = x => EMPTY.pipe(
  startWith(x),
  delay(1000)
)

delayMsg() 負責由 argument 產生 message,由 EMPTY Observable 開始建立,首先以 startWith() 建立 initial value,最後 delay 1s 才產生。

18 行

let slogan$ = concat(
  delayMsg$('Get Ready!'),
  delayMsg$(3),
  delayMsg$(2),
  delayMsg$(1),
  delayMsg$('Go!')
)

陸續將要顯示的 Get Ready!321Go! 傳給 delayMsg$() 產生 Observable。

由於 delayMsg$() 所產生的 Observable 都只 delay 1s,理論上應該同時產生,為什麼最後卻是先後出現呢 ?

因為 concat() 會依照 argument 順序先後合併,且會等前一個 Observable 結束後,才會執行下一個 Observable。

concat()
將眾多 Observable 依 argument 順序合併

concat001

ab Observabl 結束時,才會合併 xy Observable。

26 行

let message$ = this.click$.pipe(
  switchMapTo(slogan$)
)

由於 click$ 是 Observable,要由 click$ 引起 slogan$ 會造成 Higher Order Observable,因此使用 switchMapTo() 攤平。

Conclusion

  • 原本會以為要分別 delay 不同時間才能造成相同效果,但透過 concat(),由於其完成前一個 Observable 才會執行下一個 Observable 特性,因此所有 Observable 只要 delay 相同時間即可

Reference

RxJS, concat()
Learn RxJS, concat()