---
# System prepended metadata

title: "\U0001F954來說說 RxJS 的使用方式"
tags: [Angular]

---

###### tags: `Angular`
# 🥔來說說 RxJS 的使用方式

在開始分享如何使用 之前，先來簡單介紹 [RxJS](https://rxjs.dev/) 是什麼，它可以幫助我們解決什麼問題，以及它有什麼優點讓我們值得學習。

## 簡單介紹
首先，RxJS 是一套 Javascript 的函式庫，主要是用來處理非同步、事件的程式問題:
* 非同步:  AJAX / Service Work / Promise 
* 事件: 各式 DOM 事件 (click, keyup ...)

![](https://i.imgur.com/2DY58G2.png)

非同步資料的概念就是，開發者不能確定在一`時間序上的串流資料事件`上，何時會拿到資料，因此會先寫好相對印的程式邏輯，等待事件發生時執行相對應的程式就好，因此我們可能會遇到的情況，`在收到非同步的回傳資料後，要再發送一個請求`。

通常在這種情境下，很容易直覺的就寫出類似下方的情形，
```typescript=
doA() {
    doB() {
        doC() {
            
        }
    }
}
```
> 當 Callback 被一層層嵌套，導致程式碼難以閱讀和維護的情況，就被稱作 Callback Hell。

那為了避免上面的情形，我們可以學習如何適當的使用 RxJS 來幫助我們做資料的存取。
當然，除了 RxJS 也有其他方式可以幫助我們達到讓程式去巢狀的情形 (Promise, async/await...)

### 關於 Reactive Extensions (Rx)
Reactive Extensions 簡稱 ReactiveX 一般簡寫為 Rx ， [ReactiveX](https://reactivex.io/) 網站會發現，他有明確的定義 ReactiveX 本質上為一種設計理念，主要由三個核心概念組成 :

> ReactiveX is a combination of the best ideas from the **Observer pattern**, the **Iterator pattern**, and **functional programming**.

1. Observer pattern
2. Iterator pattern
3. functional programming
    
所以只要有任何的程式語言去實現 Rx ，那 Rx 的概念在任何平台上都有可能出現的。


## 如何使用
[RxJS](https://rxjs.dev/)、[RxJs Marbles](https://rxmarbles.com/)、[Reactive How](https://reactive.how/)

開始使用之前，我們先來認識會使用到的主要東西:

* Observable 可觀察物件
    - 未來將會發生的值/事件，並且可以被觀察的。
* Observer 觀察者物件
    - 知道如何收到 Observable 回傳的值。
* Subscription 訂閱物件
    - 被 Observer 觀察的 Observable，主要用來取消訂閱。
* Operators 操作符
    - 純函式(function)
    - 處理事件資料
* Subject 主體物件
    - 類似於 EventEmitter
    - 唯一能做到將廣播收到的事件資料給多位 Observer


### 先來寫段小程式

> 題目: 請將陣列內的非偶數數字乘以十後加總
> 輸入: [1,2,3,4]


第一種最常見的寫法就是直觀的用 `for + if` 寫出來:
==for+if==
```typescript=
const arr = [1,2,3,4];
let sum = 0;
for (let i = 0; i < arr.length; i++) {
    if (i%2 == 1) {
        sum += i*10;
    }
}
```

第二種，接觸過原型(prototype)方法(method)的人 可能會選擇使用 functional programming 的方式來寫:

==functional programming==
```typescript=
const arr = [1,2,3,4];
let sum = arr
    .filter((a) => a%2==1)
    .map((a) => a*10)
    .reduce((a,b) => a+b);
```

這邊我們就不仔細比較兩種寫法的差異，我們直接來看第二種的寫法，***將原資料經轉換後，變成想要的資料***，這樣說可能很抽象，所以我畫了一張**彈珠圖**。
> 彈珠圖是在表示 RxJS 的資料流時十分常見的示意圖，相關網站可以參考 [RxJS Marbles](https://rxmarbles.com/)。


![彈珠圖](https://i.imgur.com/c0282ZM.png)


那這樣我們來試著把它改成 RxJS 的寫法:
![](https://i.imgur.com/JqanLMC.png)

==RxJS==
```typescript=
concat([1,2,3,4]).pipe(
    filter(a => a%2==1),
    map(x => x*10),
    reduce((t,v) => t+v),
).subscribe();
```

### 完整程式碼
```typescript=

import { Component, OnInit } from '@angular/core';
import { concat, Observable, of } from 'rxjs';
import { delay, filter, map, reduce, tap } from 'rxjs/operators';

@Component({
  selector: 'app-rxjs',
  templateUrl: './rxjs.component.html',
  styleUrls: ['./rxjs.component.scss']
})
export class RxjsComponent implements OnInit {

  constructor() { }

  ngOnInit(): void {
    console.log('demoFucByN', this.demoFucByN());
    console.log('demoFucByFP', this.demoFucByFP());
    this.demoFucByRxJS().subscribe((res) => console.log('demoFucByRxJS', res));
  }


  // demo
  demoFucByN() {
    const arr = [1,2,3,4];
    let sum = 0;
    for (let i = 0; i < arr.length; i++) {
        if (i%2 == 1) {
            sum += i*10;
        }
    }
    return sum;
  }
  demoFucByFP() {
    const arr = [1,2,3,4];
    let sum = arr
        .filter((a) => a%2==1)
        .map((a) => a*10)
        .reduce((a,b) => a+b);
    return sum;
  }
  demoFucByRxJS() {
    return this.callApi$().pipe(
      filter(a => a%2==1),
      map(x => x*10),
      reduce((t,v) => t+v),
    )
  }

  callApi$():Observable<any> {
    const arr = [1,2,3,4]
    return concat(arr).pipe(delay(1000));
  }

}

```


### RxJS 相關參考資料

[Netflix JavaScript Talks - RxJS Version 5](https://www.youtube.com/watch?v=COviCoUtwx4)
[30 天精通 RxJS](https://ithelp.ithome.com.tw/users/20103367/ironman/1199)
[[RxJS] 轉換類型 Operators (1) - map / scan / pairwise](https://fullstackladder.dev/blog/2020/10/03/mastering-rxjs-18-map-scan-pairwise/)
[[RxJS] 轉換類型 Operators (2) - switchMap / concatMap / mergeMap / exhaustMap](https://fullstackladder.dev/blog/2020/10/04/mastering-rxjs-19-switchmap-concatmap-mergemap-exhaustmap/)