Observable 和 數組都有filter, map 等運算操作operators,具體的差別是什麼?
主要是兩點:
- 延遲運算
- 漸進式取值
延遲運算很好了解,所有 Observable 一定會等到訂閱後才開始對元素做運算,如果沒有訂閱就不會有運算的行為
var source = Rx.Observable.from([1,2,3,4,5]);
var example = source.map(x => x + 1);
上面這段代碼因為 Observable 還沒有被訂閱,是以不會真的對元素做運算,這跟數組的操作不一樣,如下
var source = [1,2,3,4,5];
var example = source.map(x => x + 1);
上面這段代碼執行完,example 就已經取得所有元素的傳回值了。
數組的運算都必須完整的運算出每個元素的傳回值并組成一個新數組,再做下一個運算。
數組的 operators 都必須完整的運算出每個元素的傳回值并組成一個數組,再做下一個 operator 的運算,我們看下面這段程式碼
var source = [1,2,3];
var example = source
.filter(x => x % 2 === 0) // 這裡會運算并傳回一個完整的數組
.map(x => x + 1) // 這裡也會運算并傳回一個完整的數組
上面這段代碼,相信讀者們都很熟悉了,大家應該都有注意到
source.filter(...)
就會傳回一整個新數組,再接下一個 operator 又會再傳回一個新的數組,這一點其實在我們實作 map 跟 filter 時就能觀察到
Array.prototype.map = function(callback) {
var result = []; // 建立新數組
this.forEach(function(item, index, array) {
result.push(callback(item, index, array))
});
return result; // 傳回新數組
}
每一次的 operator 的運算都會建立一個新的數組,并在每個元素都運算完後傳回這個新數組,我們可以用下面這張動态圖表示運算過程

image.png
Observable operator 的運算方式跟數組的是完全的不同,雖然 Observable 的 operator 也都會回傳一個新的 observable,但因為元素是漸進式取得的關系,是以每次的運算是一個元素運算到底,而不是運算完全部的元素再傳回。
var source = Rx.Observable.from([1,2,3]);
var example = source
.filter(x => x % 2 === 0)
.map(x => x + 1)
example.subscribe(console.log);
上面這段程式碼運作的方式是這樣的
- 送出
到 filter 被過濾掉1
-
到 filter 在被送到 map 轉成2
,送到 observer3
印出console.log
-
3
每個元素送出後就是運算到底,在這個過程中不會等待其他的元素運算。這就是漸進式取值的特性,不知道讀者們還記不記得我們在講 Iterator 跟 Observer 時,就特别強調這兩個 Pattern 的共同特性是漸進式取值,而我們在實作 Iterator 的過程中其實就能看出這個特性的運作方式
class IteratorFromArray {
constructor(arr) {
this._array = arr;
this._cursor = 0;
}
next() {
return this._cursor < this._array.length ?
{ value: this._array[this._cursor++], done: false } :
{ done: true };
}
map(callback) {
const iterator = new IteratorFromArray(this._array);
return {
next: () => {
const { done, value } = iterator.next();
return {
done: done,
value: done ? undefined : callback(value)
}
}
}
}
}
var myIterator = new IteratorFromArray([1,2,3]);
var newIterator = myIterator.map(x => x + 1);
newIterator.next(); // { done: false, value: 2 }
雖然上面這段代碼是一個非常簡單的示範,但可以看得出來每一次 map 雖然都會傳回一個新的 Iterator,但實際上在做元素運算時,因為漸進式的特性會使一個元素運算到底,Observable 也是相同的概念,我們可以用下面這張動态圖表示運算過程
漸進式取值的觀念在 Observable 中其實非常的重要,這個特性也使得 Observable 相較于 Array 的 operator 在做運算時來的高效很多,尤其是在處理大量資料的時候會非常明顯!
(想像一下我們今天要切五萬個大蛋糕,你會選擇切完一個請一個人拿走,還是全部切完再拿給所有人呢?哪個會比較有效率呢?)