Dart是單執行緒模型,我們寫的程式碼都執行在同一個執行緒中。如果做了耗時操作,會使應用程式阻塞。Dart中使用Future和Stream編寫非同步任務。
Future
Future是一個不會馬上完成的計算過程,說的通俗一點就是一個非同步執行過程,需要配合async和await一起使用。不會阻塞在此之後的程式碼,等待計算完成後才會返回結果。
類似於JavaScript中的Promise
和async
、await
。
用法
void main() {
requestApi();
doSomething1();
doSomething2();
}
doSomething1() {
print("doSomething1");
}
doSomething2() {
print("doSomething2");
}
/// 非同步請求資料
Future requestApi() async {
....
return
}
複製程式碼
由於requestApi()
是個非同步函式,程式執行後,不會等待函式執行完成,下面的程式碼也會立即開始執行。
等待返回結果
如果想在非同步函式返回結果後再做其他操作,可以使用then()
方法來監聽。
void main() {
requestApi().then((value) {
/// 結果返回,開始處理
showApiData(value);
});
print("程式開始執行...");
}
............
showApiData(dynamic value){
print("展示獲取的資料: $value");
}
複製程式碼
FutureBuilder
const FutureBuilder({
Key? key,
this.future,
this.initialData,
required this.builder,
})
複製程式碼
FutureBuilder會基於傳入的future的返回結果來構建Widget。
使用示例
一般用於網路請求後更新UI。
class FutureBuilderDemo extends StatefulWidget {
const FutureBuilderDemo({Key key}) : super(key: key);
@override
_FutureBuilderDemoState createState() => _FutureBuilderDemoState();
}
class _FutureBuilderDemoState extends State<FutureBuilderDemo> {
Future<String> futureData;
@override
void initState() {
super.initState();
/// ui初始化時開始網路請求資料
futureData = getData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FutureBuilder 測試'),
),
body: Container(
child: Center(
child: FutureBuilder<String>(
future: futureData,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return Text("獲取的資料==${snapshot.data}");
} else if (snapshot.hasError) {
return Icon(Icons.error_outline);
} else {
return CircularProgressIndicator();
}
}),
),
),
);
}
Future<String> getData() async {
/// 模擬網路請求,等待5秒返回結果
await Future.delayed(Duration(seconds: 5));
/// 返回一個錯誤
// return Future.error("error");
/// 返回一個成功的結果
return "Hello,world";
}
}
複製程式碼
Stream
Stream是一系列非同步事件的序列,相當於一個非同步的跌代器(Iterable)。不同於Future只是一個事件執行過程,Stream可以傳送執行多個事件。使用場景不同。
用法
建立方式有2種,一個是單訂閱的Stream,一種是能多訂閱的流,也就是可以廣播。
訂閱後(listen)會返回一個StreamSubscription,就是一個觀察者。
2種方式的構造方法都有一個可選引數sync
,預設為false。
..........
bool sync = false}) {
return sync
? _SyncStreamController<T>(....)
: _AsyncStreamController<T>(...);
複製程式碼
通過原始碼可以看出,sync
為false建立的是一個非同步事件StreamController, sync
為true時建立的是一個同步StreamController。因為大部分場景下使用Stream都是為了非同步,所以我們直接不傳入即可。
單訂閱
/// 直接使用建構函式
StreamController<String> _controller = StreamController();
StreamSubscription subscription = _controller.stream.listen((event) {
print("subscription 接收到 $event");
});
複製程式碼
廣播訂閱
void main() {
/// 使用工廠方法構造可廣播的Controller
StreamController _controller = StreamController.broadcast();
/// 使用多個觀察者訂閱同一個Stream.
StreamSubscription subscription1 =
_controller.stream.listen((event) => print("sub1 接收到 $event"));
StreamSubscription subscription2 =
_controller.stream.listen((event) => print("sub2 接收到 $event"));
StreamSubscription subscription3 =
_controller.stream.listen((event) => print("sub3 接收到 $event"));
/// 傳送事件後,所有已訂閱的觀察者的都能接收到事件。
_controller.add("Hello");
}
複製程式碼
執行多次,列印結果都如下不變:
sub1 接收到 Hello
sub2 接收到 Hello
sub3 接收到 Hello
Process finished with exit code 0
複製程式碼
結論:
事件訂閱(
listen
)的越早,接收到事件的優先順序越高。
釋放資源
使用完事件後或者在Flutter中Widget關閉時,記得也同時關閉Stream。
_controller.stream.listen((event) {}, onDone: () => print("收到onDone事件"));
_controller.close();
subscription.cancel();
複製程式碼
關閉後會自動觸發onDone回撥方法。
StreamBuilder
在介面中,一般使用StreamBuilder來來配合Stream使用。可以實現多狀態介面。
使用示例
/// 定義3種ui狀態
enum UIState { type_1, type_2, type_3 }
class StreamBuilderDemo extends StatefulWidget {
const StreamBuilderDemo({Key key}) : super(key: key);
@override
_StreamBuilderDemoState createState() => _StreamBuilderDemoState();
}
class _StreamBuilderDemoState extends State<StreamBuilderDemo> {
StreamController<UIState> _controller;
@override
void initState() {
super.initState();
/// 初始化controller
_controller = StreamController();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('StreamBuilder測試'),
),
body: Container(
// height: double.infinity,
child: Center(
child: StreamBuilder<UIState>(
/// 傳入stream
stream: _controller.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
/// hasData代表有接收到事件
var data = snapshot.data;
Widget widget;
///根據返回不回的型別,展示不同的圖片
switch (data) {
case UIState.type_1:
widget = Icon(Icons.timer, size: 100);
break;
case UIState.type_2:
widget = Icon(Icons.done, size: 100);
break;
case UIState.type_3:
widget = Icon(Icons.ac_unit, size: 100);
break;
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
widget,
Text("$data"),
],
);
} else if (snapshot.hasError) {
/// 接收到錯誤事件
return Icon(Icons.error_outline_rounded, size: 100);
} else {
/// 什麼都沒有,代表沒還有接收到事件
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
Text("初始化中,還沒接收到收到狀態"),
],
);
}
}),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => generateState(), child: Icon(Icons.add)),
);
}
/// 隨機生成不同的狀態,傳送事件
generateState() {
var randomIndex = Random().nextInt(UIState.values.length);
_controller.add(UIState.values[randomIndex]);
}
@override
void dispose() {
super.dispose();
/// 回收資源
_controller.close();
}
}
複製程式碼