###### 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/)