Dart | 什麼是Stream

🌎前言

StreamFuture都是Dart:async庫的核心API,對異步提供了很是好的支持。網絡

我思考了好久,究竟應該如何向你們介紹Stream(流)。由於Stream很是有用,它是爲處理異步事件而生的。而在應用中有大量的場景須要使用異步事件,例如請求網絡,和用戶交互等等,它們都沒法同步完成。Stream可以極大的幫助咱們處理這些問題。😍異步

可是對於剛接觸的新手來講,流確實足夠抽象,以致於你們須要花費很是多的時間來理解它。async

因此我將會盡我所能向你們介紹Stream。函數

🏊🏻‍Stream

🤔什麼是Stream

Stream很是有特色但卻不太好理解,我與其按照字面意思把它看做流,更願意把它當作一個工廠或者是機器。ui

咱們來看看這個機器它有什麼特色:

  • 它有一個入口,能夠放東西/指令(anything)
  • 這個機器不知道入口何時會放東西進來
  • 中間的機器可以生產或者加工,這應該會耗費一些時間
  • 他有一個出口,應該會有產品從那出來
  • 咱們也不知道到底何時產品會從出口出來

整個過程,時間都是一個不肯定因素,咱們隨時均可以向這個機器的入口放東西進去,放進去了之後機器進行處理,可是咱們並不知道它多久處理完。因此出口是須要專門派人盯着的,等待機器流出東西來。整個過程都是以異步的眼光來看的。spa

✈️咱們將機器模型轉化成Stream 3d

  • 這個大機器就是StreamController,它是建立流的方式之一。
  • StreamController有一個入口,叫作sink
  • sink可使用add方法放東西進來,放進去之後就再也不關心了。
  • 當有東西從sink進來之後,咱們的機器就開始工做啦,空空空。
  • StreamController有一個出口,叫作stream
  • 機器處理完畢後就會把產品從出口丟出來,可是咱們並不知道何時會出來,因此咱們須要使用listen方法一直監聽這個出口。
  • 並且當多個物品被放進來了以後,它不會打亂順序,而是先入先出。

經過這個例子,相信你們對流應該都有了基礎印象,那麼要解釋後面的東西就不難了。code

🤔如何使用Stream

得到Stream的方法:

  • 經過構造函數
  • 使用StreamController
  • IO Stream

stream有三個構造方法:orm

  • Stream.fromFuture:從Future建立新的單訂閱流,當future完成時將觸發一個data或者error,而後使用Down事件關閉這個流。cdn

  • Stream.fromFutures:從一組Future建立一個單訂閱流,每一個future都有本身的data或者error事件,當整個Futures完成後,流將會關閉。若是Futures爲空,流將會馬上關閉。

  • Stream.fromIterable:建立從一個集合中獲取其數據的單訂閱流。

Stream.fromIntreable([1,2,3]);
複製代碼

🧐監聽Stream的方法

監聽一個流最多見的方法就是listen。當有事件發出時,流將會通知listener。Listen方法提供了這幾種觸發事件:

  • onData(必填):收到數據時觸發
  • onError:收到Error時觸發
  • onDone:結束時觸發
  • unsubscribeOnError:遇到第一個Error時是否取消訂閱,默認爲false

😏 StreamController

若是你想建立一條新的流的話,很是簡單!😀 使用StreamController,它爲你提供了很是豐富的功能,你可以在streamController上發送數據,處理錯誤,並得到結果!

//任意類型的流
StreamController controller = StreamController();
controller.sink.add(123);
controller.sink.add("xyz");
controller.sink.add(Anything);

//建立一條處理int類型的流
StreamController<int> numController = StreamController();
numController.sink.add(123);
複製代碼

泛型定義了咱們能向流上推送什麼類型的數據。它能夠是任何類型!

咱們再來看看如何獲取最後的結果。

StreamController controller = StreamController();

//監聽這個流的出口,當有data流出時,打印這個data
StreamSubscription subscription =
controller.stream.listen((data)=>print("$data"));

controller.sink.add(123);
複製代碼

輸出: 123

你須要將一個方法交給stream的listen函數,這個方法入參(data)是咱們的StreamController處理完畢後產生的結果,咱們監聽出口,並得到了這個結果(data)。這裏可使用lambda表達式,也能夠是其餘任何函數。

(這裏我爲了方便區分,把listen說成函數,(data)=>print(data)說成方法,實際上是一個東西。)

🤠Transforming an existing stream

假如你已經有了一個流,你能夠經過它轉化成爲一條新的流。很是簡單!流提供了map(),where(),expand(),和take()方法,可以輕鬆將已有的流轉化爲新的流。

where

若是你想要篩選掉一些不想要的事件。例如一個猜數遊戲,用戶能夠輸入數字,當輸入正確的時候,咱們作出必定反應。而咱們必須篩選掉全部錯誤的答案,這個時候咱們可使用where篩選掉不須要的數字。

stream.where((event){...})
複製代碼

where函數接收一個事件,每當這個流有東西流到where函數的時候,這就是那個事件。咱們或許根本不須要這個事件,可是必須做爲參數傳入。

take

若是你想要控制這個流最多能傳多少個東西。好比輸入密碼,咱們可能想讓用戶最多輸四次,那麼咱們可使用take來限制。

stream.take(4);
複製代碼

take函數接收一個int,表明最多能通過take函數的事件次數。當傳輸次數達到這個數字時,這個流將會關閉,沒法再傳輸。

transform

若是你須要更多的控制轉換,那麼請使用transform()方法。他須要配合StreamTransformer進行使用。咱們先來看下面一段猜數遊戲,而後我會向你解釋。

StreamController<int> controller = StreamController<int>();

final transformer = StreamTransformer<int,String>.fromHandlers(
    handleData:(value, sink){
  	if(value==100){
      sink.add("你猜對了");
    }
	else{ sink.addError('還沒猜中,再試一次吧');
    }
  });
  
  controller.stream
            .transform(transformer)
            .listen(
                (data) => print(data),
                onError:(err) => print(err));
    
    controller.sink.add(23);
    //controller.sink.add(100);
複製代碼

輸出: 還沒猜中,再試一次吧

StreamTransformer<S,T>是咱們stream的檢查員,他負責接收stream經過的信息,而後進行處理返回一條新的流。

  • S表明以前的流的輸入類型,咱們這裏是輸入一個數字,因此是int。
  • T表明轉化後流的輸入類型,咱們這裏add進去的是一串字符串,因此是String。
  • handleData接收一個value並建立一條新的流並暴露sink,咱們能夠在這裏對流進行轉化。
  • 咱們還能夠addError進去告訴後面有問題。

而後咱們監聽transform以後的流,當轉換好的event流出時,咱們打印這個event,這個event就是咱們剛纔add進sink的數據。onError可以捕捉到咱們add進去的err。

🤨Stream的種類

流有兩種

  • "Single-subscription" streams 單訂閱流
  • "broadcast" streams 多訂閱流

"Single-subscription" streams

單個訂閱流在流的整個生命週期內僅容許有一個listener。它在有收聽者以前不會生成事件,而且在取消收聽時它會中止發送事件,即便你仍然在Sink.add更多事件。

即便在第一個訂閱被取消後,也不容許在單個訂閱流上進行兩次偵聽。

單訂閱流一般用於流式傳輸更大的連續數據塊,如文件I / O.

StreamController controller = StreamController();

controller.stream.listen((data)=> print(data));
controller.stream.listen((data)=> print(data));

controller.sink.add(123);
複製代碼

輸出: Bad state: Stream has already been listened to. 單訂閱流不能有多個收聽者。

"Broadcast" streams

廣播流容許任意數量的收聽者,且不管是否有收聽者,他都能產生事件。因此中途進來的收聽者將不會收到以前的消息。

若是多個收聽者想要收聽單個訂閱流,請使用asBroadcastStream在非廣播流之上建立廣播流。

若是在觸發事件時將收聽者添加到廣播流,則該偵聽器將不會接收當前正在觸發的事件。若是取消收聽,收聽者會當即中止接收事件。

通常的流都是單訂閱流。從Stream繼承的廣播流必須重寫isBroadcast 才能返回true。

StreamController controller = StreamController();
//將單訂閱流轉化爲廣播流
Stream stream = controller.stream.asBroadcastStream();

stream.listen((data)=> print(data));
stream.listen((data)=> print(data));

controller.sink.add(123);
複製代碼

輸出: 123 123

🎆 寫在最後

以上就是關於Dart中Stream的簡單介紹,若是你還有任何疑問或者建議,歡迎在下方評論區或者郵箱告訴我!我會在24小時內儘快聯繫您😉

下一篇文章我將向你們介紹一種很是棒的狀態管理方式,它是Google團隊力推的狀態管理方法,我認爲它真的很酷!

因此,準備好迎接BLoC了嗎😎

相關文章
相關標籤/搜索