官方文件中文翻譯:非同步程式設計:使用 stream
1、stream
是什麼
Stream
是一些列非同步事件的序列。
2、Stream
的接收
① 使用非同步for
迴圈
/// 使用非同步for迴圈來接收所有的stream
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for(var value in stream) {
sum += value;
}
return sum;
}
複製程式碼
② 使用stream
的listen
來監聽
3、stream
的種類
① 單訂閱stream
(Single-Subscription
)
只包含一個事件序列,事件需要按順序提供,不能丟失。比如讀取一個檔案,接收一個網頁。
② 廣播stream
(Broadcast
)
針對單個訊息的,一次處理一個訊息。比如瀏覽器的滑鼠事件。 可以在任何時候監聽,可以新增多個監聽,還可以隨時取消監聽。
4、處理stream
的方法
Future<T> get first;
Future<bool> get isEmpty;
Future<T> get last;
Future<int> get length;
Future<T> get single;
Future<bool> any(bool Function(T element) test);
Future<bool> contains(Object? needle);
Future<E> drain<E>([E? futureValue]);
Future<T> elementAt(int index);
Future<bool> every(bool Function(T element) test);
Future<T> firstWhere(bool Function(T element) test, {T Function()? orElse});
Future<S> fold<S>(S initialValue, S Function(S previous, T element) combine);
Future forEach(void Function(T element) action);
Future<String> join([String separator = '']);
Future<T> lastWhere(bool Function(T element) test, {T Function()? orElse});
Future pipe(StreamConsumer<T> streamConsumer);
Future<T> reduce(T Function(T previous, T element) combine);
Future<T> singleWhere(bool Function(T element) test, {T Function()? orElse});
Future<List<T>> toList();
Future<Set<T>> toSet();
複製程式碼
5、修改stream
的方法
修改stream
會產生一個新的stream
,在原來stream
上新增的監聽,會轉到新的stream
上,如果新的stream
結束了,會轉到原來的stream
上。
詳細參考文章:Flutter Stream簡介及部分操作符使用
// 將一個Stream轉為元素都為R型別的Stream
Stream<R> cast<R>();
// 把Stream中的每一個元素,轉換為一個序列sequence,比如元素1轉為[1, 1]
Stream<S> expand<S>(Iterable<S> Function(T element) convert);
// 按map的實現規則,轉換Stream中的每一個元素成為一個新的Stream
Stream<S> map<S>(S Function(T event) convert);
// 跳過前面count個事件
Stream<T> skip(int count);
// 根據傳入的條件規則,進行跳過
Stream<T> skipWhile(bool Function(T element) test);
// 指定只傳送count個事件
Stream<T> take(int count);
// 只傳送指定條件的事件
Stream<T> takeWhile(bool Function(T element) test);
// 用條件丟棄一些元素,建立一個新的Stream
Stream<T> where(bool Function(T event) test);
...
複製程式碼
6、listen
方法
StreamSubscription<T> listen(void onData(T event)?,
{Function? onError, void onDone()?, bool? cancelOnError});
複製程式碼
7、stream
的建立
① 從其他stream
轉換
以上提到的修改stream
的方法
② 使用非同步生成器(async*
)生成stream
/// 非同步生成器(async*) 生成 stream
/// 通過 yield 和 yield* 向stream提交事件
Stream<int> createAsyncStream() async* {
int num = 0;
while (true) {
// 間隔1秒鐘
await Future.delayed(Duration(seconds: 1));
// 將num運算後的值放入stream
yield num++;
// 終止條件
if (num == 10) break;
}
}
複製程式碼
③ 使用StreamController
來建立
var streamController = StreamController<int>(
onListen: () {},
onResume: () {},
onPause: () {},
onCancel: () {},
);
複製程式碼
比如,event_bus
的實現,使用廣播stream
EventBus({bool sync = false})
: _streamController = StreamController.broadcast(sync: sync);
複製程式碼
8、實際應用場景
① event_bus 外掛的使用
相關文章:Flutter EventBus 的使用和底層實現分析
- 初始化
EventBus
,會建立一個通過廣播方式初始化的StreamController
。- 訂閱監聽,
on
方法返回的是一個stream
,stream
的listen
方法傳入的對監聽到事件的處理方法。fire
方法實現,是通過往StreamController
中新增自定義的事件。
② Bloc外掛的使用
③ StreamBuilder
元件
9、相關的語法關鍵字(await、async、sync*、async*、yield、yield*)
① await
用於等待非同步方法返回資料
② async
用於非同步方法
③ sync*
多元素同步函式生成器,返回Iterable<T>
④ async*
多元素非同步函式生成器,返回Stream<T>
⑤ yield
傳送的是一個元素
⑥ yield*
操作的是一個Iterable
或Stream
具體使用例子,如下:
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: StreamPage(),
);
}
}
class StreamPage extends StatefulWidget {
const StreamPage({Key? key}) : super(key: key);
@override
_StreamPageState createState() => _StreamPageState();
}
class _StreamPageState extends State<StreamPage> {
late Stream<String> _stream;
@override
void initState() {
super.initState();
_stream = fetchEmojiStream(10);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: [
Column(
children: [
MaterialButton(
onPressed: test1,
child: Text('測試1'),
),
MaterialButton(
onPressed: test2,
child: Text('測試2'),
),
MaterialButton(
onPressed: test3,
child: Text('測試3'),
),
MaterialButton(
onPressed: test4,
child: Text('測試4'),
),
MaterialButton(
onPressed: test5,
child: Text('測試5'),
),
],
),
StreamBuilder(
stream: _stream,
builder: _builder,
),
],
),
);
}
Widget _builder(BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
break;
case ConnectionState.waiting:
return CircularProgressIndicator();
case ConnectionState.active:
return Text(snapshot.requireData);
case ConnectionState.done:
return Text(snapshot.requireData);
}
return Container();
}
void test1() async {
getEmoji(10).forEach(print);
/*
flutter: ?
flutter: ?
flutter: ?
flutter: ?
flutter: ?
flutter: ?
flutter: ?
flutter: ?
flutter: ?
flutter: ?
*/
}
void test2() {
getEmojiWithTime(10).forEach(print);
/*
flutter: ?-2021-09-02T16:41:24.557020
flutter: ?-2021-09-02T16:41:24.558755
flutter: ?-2021-09-02T16:41:24.559007
flutter: ?-2021-09-02T16:41:24.559160
flutter: ?-2021-09-02T16:41:24.559338
flutter: ?-2021-09-02T16:41:24.559515
flutter: ?-2021-09-02T16:41:24.559663
flutter: ?-2021-09-02T16:41:24.559846
flutter: ?-2021-09-02T16:41:24.560122
flutter: ?-2021-09-02T16:41:24.560372
*/
}
void test3() {
fetchEmoji(0).then(print);
/*
flutter: ?
*/
}
void test4() {
fetchEmojiStream(10).listen(print);
/* 每個一秒生成一個
flutter: ?
flutter: ?
flutter: ?
flutter: ?
flutter: ?
flutter: ?
flutter: ?
flutter: ?
flutter: ?
flutter: ?
*/
}
void test5() {
fetchEmojiWithTime(10).forEach(print);
/*
flutter: ?-2021-09-02T17:15:21.591821
flutter: ?-2021-09-02T17:15:22.595980
flutter: ?-2021-09-02T17:15:23.601100
flutter: ?-2021-09-02T17:15:24.608355
flutter: ?-2021-09-02T17:15:25.610364
flutter: ?-2021-09-02T17:15:26.612006
flutter: ?-2021-09-02T17:15:27.619303
flutter: ?-2021-09-02T17:15:28.623427
flutter: ?-2021-09-02T17:15:29.626712
flutter: ?-2021-09-02T17:15:30.629052
*/
}
/// 多元素同步
/// 多元素同步函式生成器(sync*),返回Iterable
/// yield 傳送一個元素
Iterable<String> getEmoji(int count) sync* {
Runes first = Runes('\u{1f47f}');
for (int i = 0; i < count; i++) {
yield String.fromCharCodes(first.map((e) => e + i));
}
}
/// 多元素同步
/// 多元素同步生成器(sync*),返回Iterable
/// yield* 操作的是一個Iterable
Iterable<String> getEmojiWithTime(int count) sync* {
yield* getEmoji(10).map((e) => '$e-${DateTime.now().toIso8601String()}');
}
/// 單元素非同步
/// await async
Future<String> fetchEmoji(int count) async {
await Future.delayed(Duration(milliseconds: 1000));
return String.fromCharCodes(Runes('\u{1f47f}').map((e) => e + count));
}
/// 多元素非同步
/// 多元素非同步生成器(async*),返回Stream
/// yield操作的是一個元素
Stream<String> fetchEmojiStream(int count) async* {
for (int i = 0; i < count; i++) {
yield await fetchEmoji(i);
}
}
/// 多元素非同步
/// 多元素非同步生成器(async*),返回Stream
/// yield*操作的是一個Stream
Stream<String> fetchEmojiWithTime(int count) async* {
yield* fetchEmojiStream(count).map((event) => '$event-${DateTime.now().toIso8601String()}');
}
}
複製程式碼