我們知道Dart是基于單線程模型的語言。我們在開發進行耗時操作時,比如網絡請求會阻塞我們的程式,在Dart中也有并發機制,叫做isolate。APP的啟動入口
main
函數就是一個類似Android主線程的一個主isolate。和Java的Thread不同的是,Dart中的isolate無法共享記憶體。
- isolate機制
import 'dart:io';
import 'dart:isolate';
var m;
void main() {
//建立消息接收器
m = 89;//主isolate定義m的值
var receivePort = new ReceivePort();
Isolate.spawn(entryPoint, receivePort.sendPort); //相當于java中開啟線程操作
receivePort.listen((t) {
if (t is SendPort) {
t.send("消息2"); // 通過接受子Isolate發出的sendPort來發送消息大主Isolate
} else {
print(t);
}
});
print(message);
}
var message = "hello";
void entryPoint(SendPort sendPort) {
// isolate是記憶體隔離的,mi的值是在主isolate定義的是以這裡獲得null
print(m);
sendPort.send("消息1"); //發消息到主Isolate
sleep(Duration(seconds: 10)); //延時10秒列印 消息2
//建立消息接收器
var receivePort2 = new ReceivePort();
receivePort2.listen((t) {
print(t);
});
sendPort.send(receivePort2.sendPort); //發送發送器到主Isolate,實作通信
}
- 任務隊列
同Android Handler類似,在Dart運作環境中也是靠事件驅動的,通過event loop不停的從隊列中擷取消息或者事件來驅動整個應用的運作,isolate發過來的消息就是通過loop處理。但是不同的是在Android中每個線程隻有一個Looper所對應的MessageQueue,而Dart中有兩個隊列,一個叫做event queue(事件隊列),另一個叫做microtask queue(微任務隊列)。
Dart在執行完main函數後,就會由Loop開始執行兩個任務隊列中的Event。首先Loop檢查微服務隊列,依次執行Event,當微服務隊列執行完後,就檢查Event queue隊列依次執行,在執行Event queue的過程中,每執行完一個Event就再檢查一次微服務隊列。是以微服務隊列優先級高,可以利用微服務進行插隊。
import 'dart:isolate';
void main() {
var receivePort = new ReceivePort();
receivePort.sendPort.send("消息");
receivePort.listen((t) {
print(t);
});
Future.microtask(() { //優先上面執行
print("微任務隊列");
});
}
-
Future
在 Dart 庫中随處可見 Future 對象,通常異步函數傳回的對象就是一個 Future。 當一個 future 執行完後,他裡面的值 就可以使用了,可以使用
來在 future 完成的時候執行其他代碼。Future對象其實就代表了在事件隊列中的一個事件的結果。then()
的傳回值同樣是一個future對象,可以利用隊列的原理進行組合異步任務then()
List s = ["yuuu", "rtrt", "ewe", "wew"];
Future.forEach(s, (t) {
print(t);
});
new File(r"E:\common\記錄(舊).txt").readAsString().then((String s) {
print(s);
});
上面的方式是等待執行完成讀取檔案之後,再執行一個新的future。如果我們需要等待一組任務都執行完成再統一處理一些事情,可以通過
wait()
完成。
Future readDone = new File("/Users/enjoy/a.txt").readAsString();
//延遲3s
Future delayedDone = Future.delayed(Duration(seconds: 3));
Future.wait([readDone, delayedDone]).then((values) {
print(values[0]);//第一個future的結果
print(values[1]);//第二個future的結果
});
-
Steam
Stream(流) 在 Dart API 中也經常出現,表示發出的一系列的異步資料。 Stream 是一個異步資料源,它是 Dart 中處理異步事件流的統一 API。
Future 表示稍後獲得的一個資料,所有異步的操作的傳回值都用 Future 來表示。但是 Future 隻能表示一次異步獲得的資料。而 Stream 表示多次異步獲得的資料。比如 IO 處理的時候,每次隻會讀取一部分資料和一次性讀取整個檔案的内容相比,Stream 的好處是處理過程中記憶體占用較小。而 File 的
是一次性讀取整個檔案的内容進來,雖然獲得完整内容處理起來比較友善,但是如果檔案很大的話就會導緻記憶體占用過大的問題。readAsString()
Stream<List<int>> steam = new File(
r"E:\common\記錄(舊).txt").openRead();//讀取流檔案
var file = new File(r"E:\common\apk\20180823\test.txt");
var openWrite = file.openWrite();
openWrite.addStream(steam);//寫入流檔案
var listen = steam.listen((List<int> bytes) {
print(12131); //執行多次,在檔案較大時候每次讀取64k的大小
});
listen.onData((_){
print("替代listene");
});
listen.onDone((){
print("結束");
});
listen.onError((e,s){
print("異常");
});
//暫停,如果沒有繼續則會退出程式
listen.pause();
//繼續
listen.resume();
-
廣播模式
Stream有兩種訂閱模式:單訂閱和多訂閱。單訂閱就是隻能有一個訂閱者,上面的使用我們都是單訂閱模式,而廣播是可以有多個訂閱者。通過 Stream.asBroadcastStream() 可以将一個單訂閱模式的 Stream 轉換成一個多訂閱模式的 Stream,isBroadcast 屬性可以判斷目前 Stream 所處的模式。
var stream = new File("/Users/enjoy/app-release.apk").openRead();
stream.listen((List<int> bytes) {
});
//錯誤 單訂閱隻能有一個訂閱者
// stream.listen((_){
// print("stream執行");
// });
var broadcastStream = new File("/Users/enjoy/app-release.apk").openRead().asBroadcastStream();
broadcastStream.listen((_){
print("訂閱者1");
});
broadcastStream.listen((_){
print("訂閱者2");
});
需要注意的是,多訂閱模式如果沒有及時添加訂閱者則可能丢資料。
//預設是單訂閱
var stream = Stream.fromIterable([1, 2, 3]);
//3s後添加訂閱者 不會丢失資料
new Timer(new Duration(seconds: 3), () => stream.listen(print));
//建立一個流管理器 對一個stream進行管理
var streamController = StreamController.broadcast();
//添加
streamController.add(1);
//先發出事件再訂閱 無法接到通知
streamController.stream.listen((i){
print("broadcast:$i");
});
//記得關閉
streamController.close();
//這裡沒有丢失,因為stream通過asBroadcastStream轉為了多訂閱,但是本質是單訂閱流,并不改變原始 stream 的實作特性
var broadcastStream = Stream.fromIterable([1, 2, 3]).asBroadcastStream();
new Timer(new Duration(seconds: 3), () => broadcastStream.listen(print));
-
async/await
使用
和async
的代碼是異步的,但是看起來很像同步代碼。當我們需要獲得A的結果,再執行B,時,你需要await
,但是利用then()->then()
與async
能夠非常好的解決回調地獄的問題:await
//async 表示這是一個異步方法,await必須再async方法中使用
//異步方法隻能傳回 void和Future
Future<String> readFile() async {
//await 等待future執行完成再執行後續代碼
String s= await new File( r"E:\common\記錄(舊).txt").readAsString();
String s2 = await new File( r"E:\common\記錄(舊).txt").readAsString();
//自動轉換為 future
return s;
}