《響應式程式設計(Reactive Programming)介紹》文章總結與案例分析

BIRD發表於2018-04-07

最近一直在研究響應式程式設計,讀到一篇文章響應式程式設計(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,相當於呼叫了快取的資料.

如有錯誤,歡迎指正!

參考

響應式程式設計(Reactive Programming)介紹

rxjs簡單入門

相關文章