瞭解 rxjs 中的 defer
作者:阿古達木
來源:SegmentFault 思否社區
下面介紹一個少有人知道的 observable -- defer,如何使用,什麼時候使用
讀這篇文章之前,你需要對 rxjs 基礎用法有一定的瞭解
假設我們需要寫一個自定義 operator 叫做 tapOnce。接收一個函數當作參數,只有流的第一次觸發時才執行
function tapOnce(fn: Function) {
let run = false;
return function (source: Observable<any>) {
return source.pipe(
tap((data) => {
if (!run) {
fn(data);
run = true;
}
})
);
};
}
這段代碼簡單直觀,在 tap 的基礎上,用了一個變量來控制執行次數,調用一下
const test$ = interval(1000).pipe(tapOnce((d) => console.log('🐶', d)));
test$.subscribe();
// 1s之後打印 🐶0
運行很正常,在流第一次觸發的時候打印狗頭。
要是再加一個訂閱者呢?
const test$ = interval(1000).pipe(tapOnce((d) => console.log('🐶', d)));
test$.subscribe();
test$.subscribe();
// 1s之後打印 🐶0
結果只打印了一遍,這是因爲兩個訂閱者訂閱同一個流,使用同一個 run 變量。
想要打印兩遍,我們就需要一個能夠在訂閱時才創建流的功能。
defer 就是用來做這件事的
改進一下
function tapOnce(fn: Function) {
return function (source: Observable<any>) {
return defer(() => {
let run = false;
return source.pipe(
tap((data) => {
if (!run) {
fn(data);
run = true;
}
})
);
});
};
}
defer 接收一個返回類型爲 observable 的函數。只有當 defer 被訂閱了,函數纔會執行。而不是在創建時。然後利用 js 閉包,讓每個訂閱者有自己的作用域。
通過簡單的抽象類看一下 defer 到底是怎麼實現的
function defer(observableFactory: () => ObservableInput<any>) {
return new Observable(subscriber => {
const source = observableFactory();
return source.subscribe(subscriber);
});
}
defer 返回一個新的 observable。當有訂閱者訂閱的時候,就會執行工廠方法,創建並返回新的 observalbe。
看看 defer 還能在什麼場景下發揮作用,假設有一個這樣的需求,每次訂閱的時候返回一個隨機數
const randNum = of(Math.random());
randNum.subscribe(console.log);
randNum.subscribe(console.log);
這裏每一個訂閱者打印的值是一樣的,我們可以用 defer 改進一下
const randNum = defer(() => of(Math.random()));
randNum.subscribe(console.log);
randNum.subscribe(console.log);
// 等同於這種寫法
const randNum2 = () => of(Math.random());
randNum2().subscribe(console.log);
randNum2().subscribe(console.log);
另一種場景是我們想要延遲一下 promise 的執行時間。當有訂閱者的時候,promise 才執行。實現一個 lazyPromise
// 此時console.log('promise')已經執行
const promise = new Promise((resolve) => {
console.log('promise');
setTimeout(() => resolve('promise'), 1000);
});
// console.log('lazy promise');只有當被訂閱才執行
const lazyPromise = defer(() => {
return new Promise((resolve) => {
console.log('lazy promise');
setTimeout(() => resolve('promise'), 1000);
});
});
lazyPromise.subscribe(console.log);
Promises 天生就是熱流,無視訂閱者。我們可以通過 defer,把 promise 變成一個 Observable-like
代碼:https://stackblitz.com/edit/rxjs-zw5ujo?file=index.ts
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/3N-5lTvNtNewaN9NuKLHbg