摘要:函數調用后同步計算并返回單一值生成器函數遍歷器遍歷過程中同步計算并返回個到無窮多個值異步執行中返回或者不返回單一值同步或者異步計算并返回個到無窮多個值是函數概念的拓展既不像,也不像是。如果不調用函數,就不會執行如果如果不訂閱,也不會執行。
Observable(可觀察對象)是基于推送(Push)運行時執行(lazy)的多值集合。下方表格對Observable進行了定位(為解決基于推送的多值問題):
MagicQ | 單值 | 多值 |
---|---|---|
拉取(Pull) | 函數 | 遍歷器 |
推送(Push) | Promise | Observable |
例:當observable被訂閱后,會立即(同步地)推送1, 2, 3 三個值;1秒之后,繼續推送4這個值,最后結束(推送結束通知):
var observable = Rx.Observable.create(function (observer) { observer.next(1); observer.next(2); observer.next(3); setTimeout(() => { observer.next(4); observer.complete(); }, 1000); });
為得到observable推送的值,我們需要訂閱(subscribe)這個Observable:
var observable = Rx.Observable.create(function (observer) { observer.next(1); observer.next(2); observer.next(3); setTimeout(() => { observer.next(4); observer.complete(); }, 1000); }); console.log("just before subscribe"); observable.subscribe({ next: x => console.log("got value " + x), error: err => console.error("something wrong occurred: " + err), complete: () => console.log("done"), }); console.log("just after subscribe");
程序執行后,將在控制臺輸出如下結果:
just before subscribe got value 1 got value 2 got value 3 just after subscribe got value 4 done拉取(Pull) V.S. 推送(Push)
拉取和推送是數據生產者和數據消費者之間通信的兩種不同機制。
何為拉取? 在拉取系統中,總是由消費者決定何時從生產者那里獲得數據。生產者對數據傳遞給消費者的時間毫無感知(被動的生產者,主動的消費者)。
JavaScript函數是典型的拉取系統:函數是數據的生產者,對函數進行調用的代碼(消費者)從函數調用后的返回值中拉取單值進行消費。
// 函數是數據的生產者 let getLuckyNumber = function() { return 7; }; /* let代碼段是數據的消費者, * getLuckyNumber對調用時間毫無感知。 */ let luckNumber = getLuckyNumber();
ES2015 引入了的 生成器函數 | 遍歷器 (function*)同樣是基于拉取的系統: 調用 iterator.next()的代碼段是消費者,它可以從生成器函數中拉取多個值。
function* getLessThanTen() { var i = 0; while(i < 11) { yield i++; } } // 生產者 let iterator = getLessThanTen(); // 消費者 iterator.next(); // Object {value: 0, done: false} iterator.next(); // Object {value: 1, done: false}
MagicQ | 生產者 | 消費者 |
---|---|---|
拉取 | 被動: 在被請求時產生數據 | 主動: 決定何時請求數據 |
推送 | 主動: 控制數據的產生邏輯 | 被動: 獲得數據后進行響應 |
何為推送? 在推送系統中生產者決定何時向消費者傳遞數據,消費者對何時收到數據毫無感知(被動的消費者)。
現代JavaScript中Promise是典型的推送系統。作為數據生產者的Promise通過resolve()向數據消費者——回調函數傳遞數據:與函數不同,Promise決定向回調函數推送值的時間。
RxJS在 JavaScript 中引入了Observable(可觀察對象)這個新的推送系統。Observable是多數據值的生產者,向Observer(被動的消費者)推送數據。
函數 調用后同步計算并返回單一值
生成器函數 | 遍歷器 遍歷過程中同步計算并返回0個到無窮多個值
Promise 異步執行中返回或者不返回單一值
Observable 同步或者異步計算并返回0個到無窮多個值
Observable 是函數概念的拓展Observable既不像EventEmitter,也不像是Promise。Observable 中的 Subject 進行多路推送時與 EventEmitter 行為上有些類似,但是實際上Observable與EventEmitter并不相同。
Observable 更像是一個不需要傳入參數的函數,它拓展了函數的概念使得它可以返回多個值。
看看下面的例子:
function foo() { console.log("Hello"); return 42; } var x = foo.call(); // same as foo() console.log(x); var y = foo.call(); // same as foo() console.log(y);
輸出結果如下:
"Hello" 42 "Hello" 42
通過Observable可以實現同樣的行為:
var foo = Rx.Observable.create(function (observer) { console.log("Hello"); observer.next(42); }); foo.subscribe(function (x) { console.log(x); }); foo.subscribe(function (y) { console.log(y); });
輸出結果相同:
"Hello" 42 "Hello" 42
不論Observable還是函數都是在運行時進行求值計算的。如果不調用函數,console.log("Hello")就不會執行;如果如果不subscribe(訂閱)Observable,console.log("Hello")也不會執行。此外,調用或者訂閱都是獨立的:兩次調用產生兩個獨立的作用域,兩次訂閱同樣會產生兩個獨立的作用域。EventEmitter總是在同一個作用域中,發射前也不會在意自己是否已經被訂閱;Observable不會被共享而產生副作用,并且總是在被訂閱時才執行。
訂閱Observable與調用函數類似。
一些人認為Observable總是是異步的,這個觀點并不正確,如果在控制臺log函數中調用函數:
console.log("before"); console.log(foo.call()); console.log("after");
顯然可以看到以下輸出:
"before" "Hello" 42 "after"
Observable的行為完全一樣:
console.log("before"); foo.subscribe(function (x) { console.log(x); }); console.log("after");
輸出結果為:
"before" "Hello" 42 "after"
訂閱 foo完全是同步的,與函數的調用一樣。
Observable可以異步或者同步地產生數據。
那Observable 與函數的不同之處在哪里? Observable可以在一個時間過程中‘返回’多個值,而函數卻不能。在函數中你不可以這么做:
function foo() { console.log("Hello"); return 42; return 100; // 這個語句永遠不會被執行。 }
雖然函數只能有一個返回值,但是在Observable中你完全可以這么做:
var foo = Rx.Observable.create(function (observer) { console.log("Hello"); observer.next(42); observer.next(100); // 返回另一個值 observer.next(200); // 返回另一個值 }); console.log("before"); foo.subscribe(function (x) { console.log(x); }); console.log("after");
輸出結果如下:
"before" "Hello" 42 100 200 "after"
你甚至可以異步地返回值:
var foo = Rx.Observable.create(function (observer) { console.log("Hello"); observer.next(42); observer.next(100); observer.next(200); setTimeout(() => { observer.next(300); // happens asynchronously }, 1000); }); console.log("before"); foo.subscribe(function (x) { console.log(x); }); console.log("after");
輸出結果:
"before" "Hello" 42 100 200 "after" 300
結論:
func.call() 意味著“同步地給我一個值”
observable.subscribe() 意味著“不管是同步或者異步,給我一些值”
Observable 剖析通過使用 Rx.Observable.create 或者是創建操作符,創建一個Observable; Observable 被 Observer(觀察者) 訂閱; 在執行時 向觀察者發送next / error / complete 通知;同時執行過程可以被 終止。
Observable 類型的實例具備了以上四個方面的特性,與其他類型如:Observer 和 Subscription 緊密相關。
我們重點關注以下四個方面:
創建
訂閱
執行
終止
創建Rx.Observable.create 是 Observable 構造函數的別名,接受一個參數: subscribe函數。
以下例子會創建一個Observable,每一秒鐘向其訂閱者發射一個"hi" 字符串。
var observable = Rx.Observable.create(function subscribe(observer) { var id = setInterval(() => { observer.next("hi") }, 1000); });
除了使用create創建Observable,我們通常還使用創建操作符, 如 of,from, interval, 等來創建Observable。
上面例子中,subscribe函數是定義Observable最重要的部分。我們接下來了解訂閱的含義。
訂閱上面例子中的observable 可以以如下方式 訂閱 :
observable.subscribe(x => console.log(x));
observable.subscribe 和 Observable.create(function subscribe(observer) {...})中的subscribe 同名并非巧合。雖然在Rx中它們不是同一個對象,但是在工程中,我們可以在概念上視兩者為等價物。
調用subscribe的觀察者并不會共享同一個Observable。觀察者調用observable.subscribe 時,Observable.create(function subscribe(observer) {...})中的subscribe會在調用它的觀察者作用域中執行。每一次observable.subscribe的調用,都是彼此獨立的。
訂閱Observable如同調用函數,需要提供相應的回調方法。
訂閱機制與處理事件的addEventListener / removeEventListenerAPI完全不同。通過observable.subscribe,觀察者并不需要在Observable中進行注冊,Observable也不需要維護訂閱者的列表。
訂閱后便進入了Observable的執行階段,在執行階段值和事件將會被傳遞給觀察者供其消費。
執行只有在被訂閱之后Observable才會執行,執行的邏輯在Observable.create(function subscribe(observer) {...})中描述,執行后將會在特定時間段內,同步或者異步地成產多個數據值。
Observable在執行過程中,可以推送三種類型的值:
"Next" 通知: 實際產生的數據,包括數字、字符串、對象等
"Error" 通知:一個JavaScript錯誤或者異常
"Complete" 通知:一個不帶有值的事件
“Next” 通知是最重要和常用的類型:表示事件傳遞給觀察者的數據。錯誤和完成通知僅會在執行階段推送其一,并不會同時推送錯誤和完成通知。
通過所謂的“Observable語法”或者“契約”可以最好地表達這個規則,“Observable語法”借助于正則表達式:
next*(error|complete)?
在Observable的執行過程中,0個或者多個“Next”通知會被推送。在錯誤或者完成通知被推送后,Observable不會再推送任何其他通知。
下面代碼展示了Observable 在執行過程中推送3個“Next” 通知然后結束:
var observable = Rx.Observable.create(function subscribe(observer) { observer.next(1); observer.next(2); observer.next(3); observer.complete(); });
Observable 嚴格遵守 Observable 契約,后面值為4的“Next” 通知永遠不會被推送:
var observable = Rx.Observable.create(function subscribe(observer) { observer.next(1); observer.next(2); observer.next(3); observer.complete(); observer.next(4); // 由于違法契約,4不會被推送 });
使用try/catch塊包裹 subscribe 代碼是一個很贊的想法,如果捕獲了異常,可以推送錯誤通知:
var observable = Rx.Observable.create(function subscribe(observer) { try { observer.next(1); observer.next(2); observer.next(3); observer.complete(); } catch (err) { observer.error(err); // 捕獲異常后推送錯誤通知 } });終止
Observable的執行可能是無限的,作為觀察者需要主動中斷執行:我們需要特定的API去終止執行過程。因為特定的觀察者都有特定的執行過程,一旦觀察者獲得想要的數據后就需要終止執行過程以免帶來計算時對內存資源的浪費。
在observable.subscribe被調用時,觀察者會與其執行作用域綁定,同時返回一個Subscription類型的對象:
var subscription = observable.subscribe(x => console.log(x));
Subscription對象表示執行過程,通過極簡的API,你可以終止執行過程。詳情請閱讀Subscription 相關文檔。通過調用subscription.unsubscribe() 你可以終止執行過程:
var observable = Rx.Observable.from([10, 20, 30]); var subscription = observable.subscribe(x => console.log(x)); // Later: subscription.unsubscribe();
在Observable被訂閱后,代表執行過程的Subscription 對象將被返回。對其調用unsubscribe()就可以終止執行。
每一個Observable都需要在 create()的創建過程中定義終止的邏輯。在function subscribe()中返回自定義的unsubscribe就可以實現。
下面的例子說明了如何在終止后釋放setInterval的句柄:
var observable = Rx.Observable.create(function subscribe(observer) { // 獲得定時函數的句柄 var intervalID = setInterval(() => { observer.next("hi"); }, 1000); // 提供終止方法釋放定時函數的句柄 return function unsubscribe() { clearInterval(intervalID); }; });
類似于observable.subscribe 和 Observable.create(function subscribe() {...})的關系,我們在subscribe中返回的 unsubscribe 也與subscription.unsubscribe在概念上等價。事實上,如果我們除去Rx的包裝,純粹的JavaScript代碼簡單清晰:
function subscribe(observer) { var intervalID = setInterval(() => { observer.next("hi"); }, 1000); return function unsubscribe() { clearInterval(intervalID); }; } var unsubscribe = subscribe({next: (x) => console.log(x)}); // 一段時間后: unsubscribe(); // 終止
使用Observable、 Observer 和 Subscription這些概念的原因是,我們可以在Observable 契約之下安全、兼容地調用操作符。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79350.html
摘要:返回的對象同時是類型的,擁有方法。由于調用后,開始執行,因此,會返回一個供調用者來終止執行。是的一個衍生類,具有最新的值的概念。舉一個形象的例子,表示一個人的生日,而則表示一個人的歲數。 什么是Subject? 在RxJS中,Subject是一類特殊的Observable,它可以向多個Observer多路推送數值。普通的Observable并不具備多路推送的能力(每一個Observer...
摘要:發布通過回調方法向發布事件。觀察者一個回調函數的集合,它知道如何去監聽由提供的值。 本文目錄 一、項目起步 二、編寫路由組件 三、編寫頁面組件 1.編寫單一組件 2.模擬數據 3.編寫主從組件 四、編寫服務 1.為什么需要服務 2.編寫服務 五、引入RxJS 1.關于RxJS 2.引入RxJS 3.改造數據獲取方式 六、改造組件 1.添...
摘要:在中,是一個由回調函數組成的對象,鍵名分別為和,以此接受推送的不同類型的通知,下面的代碼段是的一個示例調用邏輯,只需在訂閱后將傳入在中,是可選的。當然你也可以將和的回調函數分別傳入什么是是一個代表可以終止資源的對象,表示一個的執行過程。 Observer(觀察者) 什么是Observer? Observer(觀察者)是Observable(可觀察對象)推送數據的消費者。在RxJS中,O...
摘要:項目簡介本次使用了和開發了一個地址輸入框,主要實現的功能有限制輸入符合條件的字符并每隔兩位可以自動添加用于分割的冒號。項目屏蔽了的事件處理,同時使用來手動控制光標。繼承于和因此同時具有和兩者的方法。后面的和都是需要利用最新的來進行判斷的。 項目簡介 本次使用了RxJS和react開發了一個mac地址輸入框,主要實現的功能有限制輸入符合條件的字符1-9,a-f,并每隔兩位可以自動添加用于...
摘要:是的縮寫,起源于,是一個基于可觀測數據流結合觀察者模式和迭代器模式的一種異步編程的應用庫。是基于觀察者模式和迭代器模式以函數式編程思維來實現的。學習之前我們需要先了解觀察者模式和迭代器模式,還要對流的概念有所認識。 RxJS 是 Reactive Extensions for JavaScript 的縮寫,起源于 Reactive Extensions,是一個基于可觀測數據流 Stre...
閱讀 2067·2021-11-23 09:51
閱讀 3358·2021-09-28 09:36
閱讀 1129·2021-09-08 09:35
閱讀 1771·2021-07-23 10:23
閱讀 3268·2019-08-30 15:54
閱讀 3005·2019-08-29 17:05
閱讀 445·2019-08-29 13:23
閱讀 1300·2019-08-28 17:51