最近一直在研究響應式程式設計,讀到一篇文章響應式程式設計(Reactive Programming)介紹,由極客學院翻譯的,非常感謝極客學院!
英文原文The introduction to Reactive Programming you've been missing.
文章較長,仔細研究了這篇文章,提煉一些東西和總結一下自己的實踐與理解.
1.思維轉變:一切皆物件=>一切皆流
我理解的流:一段時間內的不同狀態. 字串、數字、事件、請求等等,一切都是可以轉化成流
from、of等方法建立的流,也是一個流,只是這個流中只有一種狀態,
2.流也有不同
1:發射完資料更新自動關閉:from, fromPromise, of, range 2:保持發射資料且不自動關閉:timer, interval, fromEvent 3:需要手動發射資料且不自動關閉:create 4:不發射直接關閉:empty 5:丟擲異常後關閉:throw 6:不發射資料也不關閉:never
不關閉的流就需要手動執行unsubscribe,有一些框架已經幫你自動關閉了一些流,比如Angular中AsyncPipe在元件銷燬的時候會自動取消訂閱,參考:Angular 中何時取消訂閱
此處有疑問,還請高手指教:在Angular中怎麼從原始碼中找出此處需要手動執行unsubscribe還是自動?是看原始碼中有在ngOndestory的時候是否執行了unsubscribe嗎? 不甚感激!
3.設計流的時候也可以反向推理,從最終獲取的資料往前一步,正反向結合
比如文章中 suggestion1Stream=>responseStream=>requestStream
4.嘗試畫rxjs資料流向圖,來整理設計流;看懂rxjs互動圖
比如
refreshClickStream: ----------o---------o---->
requestStream: -r--------r---------r---->
responseStream: ----R----------R------R-->
suggestion1Stream: -N--s-----N----s----N-s-->
suggestion2Stream: -N--q-----N----q----N-q-->
suggestion3Stream: -N--t-----N----t----N-t-->
複製程式碼
source1: ————————①——————————②——————————③————————————④—————————⑤——————————|——>
source2: ———————————ⓐ————————ⓑ————————————ⓒ—————————————————————ⓓ—————————|——>
combineLastest(source1, source2, (x, y) => x + y)
source: ———————(①ⓐ)—(②ⓐ)—(②ⓑ)—————(③ⓑ)—(③ⓒ)———(④ⓒ)————(⑤ⓒ)—(⑤ⓓ)——|——>
複製程式碼
5.文章案例實踐及分析
1)初始化頁面和點選重新整理獲取資料
var refreshButton = document.querySelector('.refresh');
var closeButton1 = document.querySelector('.close1');
var refreshClickStream = Rx.Observable.fromEvent(refreshButton, 'click');
var requestStream = refreshClickStream.startWith('fff')
.map(function() {
var randomOffset = Math.floor(Math.random() * 500);
console.log(11)
return 'https://api.github.com/users?since=' + randomOffset;
})
var responseStream = requestStream.flatMap(function(requestUrl) {
console.log(requestUrl)
return Rx.Observable.fromPromise($.getJSON(requestUrl));
})
responseStream.subscribe(function (response) {
console.log(response)
})
複製程式碼
responseStream訂閱的時候,資料來源於requestStream,requestStream執行操作符startWith('fff'),即requestStream中發射了一次資料,資料再經過map(獲取請求地址)=>flatmap(獲取到請求的資料).
注意:文中首先把startWith放在map操作符後,這是兩種不同的資料流了
後者初始化頁面時時,requestStream來自於點選重新整理流,此時未點選,因為map無資料,所以startWith加在map後面時,需要的引數是請求的地址,以用來執行flatMap
2)渲染第一行資料
var refreshButton = document.querySelector('.refresh');
var closeButton1 = document.querySelector('.close1');
var refreshClickStream = Rx.Observable.fromEvent(refreshButton, 'click');
var requestStream = refreshClickStream.startWith('fff').map(function() {
var randomOffset = Math.floor(Math.random() * 500);
console.log(11)
return 'https://api.github.com/users?since=' + randomOffset;
})
var responseStream = requestStream.flatMap(function(requestUrl) {
console.log(requestUrl)
return Rx.Observable.fromPromise($.getJSON(requestUrl));
})
var suggestion1Stream = responseStream.map(function(listUsers) {
return listUsers[Math.floor(Math.random() * listUsers.length)];
}).merge(refreshClickStream.map(function(event) {
console.log(event) // click的event
return null;
}))
suggestion1Stream.subscribe(function(aa) {
console.log(aa) // output:首先null,請求到資料後=response
})
複製程式碼
反向推資料流: suggestion1Stream=>responseStream=>requestStream=>refreshClickStream merge操作符,使suggestion1Stream資料來源於2個,點選的時候首先merge refreshClickStream,返回是空,所以立即清空資料,待請求成功後獲取到資料再賦值成功.
3)更換本行推薦
var refreshButton = document.querySelector('.refresh');
var closeButton1 = document.querySelector('.close1');
var refreshClickStream = Rx.Observable.fromEvent(refreshButton, 'click');
var close1ClickStream = Rx.Observable.fromEvent(closeButton1, 'click');
var requestStream = refreshClickStream.startWith('fff')
.map(function() {
var randomOffset = Math.floor(Math.random() * 500);
console.log(11)
return 'https://api.github.com/users?since=' + randomOffset;
})
var responseStream = requestStream.flatMap(function(requestUrl) {
console.log(requestUrl)
return Rx.Observable.fromPromise($.getJSON(requestUrl));
})
suggestion1Stream = close1ClickStream.combineLatest(responseStream, function(event, listUsers) {
console.log(event)
return listUsers[Math.floor(Math.random() * listUsers.length)];
}).merge(refreshClickStream.map(function() {
return null;
})).startWith('null')
suggestion1Stream.subscribe(function(data) {
console.log(data)
})
複製程式碼
通過combineLatest,返回組合值,但此時responseStream的狀態無變化,close1ClickStream狀態變化,返回的其實就是responseStream,相當於呼叫了快取的資料.
如有錯誤,歡迎指正!