對於異步不太瞭解,能夠直接看官網介紹;java
Dart 有一些語言特性來支持異步編程。最多見的特性是 async
方法和 await
表達式。web
Dart 庫中有不少返回 Future 或者 Stream 對象的方法。 這些方法是異步的 : 這些函數在設置完基本的操做後就返回了 , 而無需等待操做執行完成。 例如讀取一個文件 , 在打開文件後就返回了。編程
有兩種方式可使用 Future 對象中的數據:瀏覽器
使用 async
和 await
的代碼是異步的, 可是看起來有點像同步代碼。 例如,下面是一些使用 await
來 等待異步方法返回的示例:bash
await lookUpVersion();
複製代碼
要使用 await
,其方法必須帶有 async
關鍵字:markdown
checkVersion() saync {
var version = await lookUpVersion();
if(version == expectedVersion){
// TODO ------
} else {
// TODO ------
}
}
複製代碼
可使用 try
, catch
, 和 finally
來處理使用 await
的異常:網絡
void getHttp(){
try{
var response = await Dio().get("http://www.baidu.com");
print(response);
}catch(e){
print(e);
}
}
複製代碼
聲明異步方法多線程
一個 async 方法 是函數體被標記爲 async 的方法。 雖然異步方法的執行可能須要必定時間,可是 異步方法馬上返回 - 在方法體還沒執行以前就返回了。app
void getHttp async {
// TODO ---
}
複製代碼
在一個方法上添加 async 關鍵字,則這個方法返回值爲 Future。 例如,下面是一個返回字符串的同步方法:異步
String loadAppVersion() => "1.0.2"
複製代碼
若是使用 async 關鍵字,則該方法返回一個 Future,而且 認爲該函數是一個耗時的操做。
Futre<String> loadAppVersion() async => "1.0.2"
複製代碼
注意,方法的函數體並不須要使用 Future API。 Dart 會自動在須要的時候建立 Future 對象。
void main() {
//調用異步方法
doAsync();
}
// 在函數上聲明瞭 async 代表這是一個異步方法
Future<bool> doAsync() async {
try {
// 這裏是一個模擬請求一個網絡耗時操做
var result = await getHttp();
//請求出來的結果
return printResult(result);
} catch (e) {
print(e);
return false;
}
}
//將請求出來的結果打印出來
Future<bool> printResult(summary) {
print(summary);
}
//開始模擬網絡請求 等待 5 秒返回一個字符串
getHttp() {
return new Future.delayed(Duration(seconds: 5), () => "Request Succeeded");
}
複製代碼
void main() {
doAsync();
}
Future<String> doAsync() async {
return getHttp().then((r){
return printResult(r);
}).catchError((e){
print(e);
});
}
Future<String> printResult(summary) {
print(summary);
}
Future<String> getHttp() {
return new Future.delayed(Duration(seconds: 5), () => "Request Succeeded");
}
複製代碼
先來看一段代碼
void doAsyncs() async{
//then catchError whenComplete
new Future(() => futureTask()) // 異步任務的函數
.then((m) => "1-:$m") // 任務執行完後的子任務
.then((m) => print('2-$m')) // 其中m爲上個任務執行完後的返回的結果
.then((_) => new Future.error('3-:error'))
.then((m) => print('4-'))
.whenComplete(() => print('5-')) //不是最後執行whenComplete,一般放到最後回調
// .catchError((e) => print(e))//若是不寫test默認實現一個返回true的test方法
.catchError((e) => print('6-catchError:' + e), test: (Object o) {
print('7-:' + o);
return true; //返回true,會被catchError捕獲
// return false; //返回false,繼續拋出錯誤,會被下一個catchError捕獲
})
.then((_) => new Future.error('11-:error'))
.then((m) => print('10-'))
.catchError((e) => print('8-:' + e))
;
}
futureTask() {
// throw 'error';
return Future.delayed(Duration(seconds: 5),() => "9-走去跑步");
}
複製代碼
你們猜一猜,看下是怎麼樣的一個執行流程;我直接放上答案吧,咱們來分析下
2-1-:9-走去跑步
5-
7-:3-:error
6-catchError:3-:error
8-:11-:error
複製代碼
當異步函數 futureTask() 執行完會在內存中保存 ‘9-走去跑步’ 而後繼續執行下一步 這個時候碰見了 then 如今會在內存中保存 「1-: 9-走去跑步 」 繼續執行 這個時候碰見了打印輸出 2-1-:9-走去跑步 。如今第一個打印出來了。接着執行下一個 then() 這個時候碰見了一個 error 異常,Dart 會把這個異常保存在內存直到碰見捕獲異常的地方。下面執行 whenComplete 這個函數 打印 5- 。而後碰見了一個捕獲異常的函數 catchError 若是 test 返回 true ,會被 catchError 捕獲 打印 7-:3-:error 6-catchError:3-:error。若是返回 false 只打印 7-:3-:error,會把 error 拋給下一個 catchError 。繼續執行 又碰見了一個 error 11-:error ,如今出現 error 了 因此 then 10- 就不會執行了 。最後就直接捕獲異常 打印 "8-11-error"。
分析完了,你們會了嗎?相信你們差很少會了!
Dart 是單線程模型 Main, 也就沒有了所謂的主線程/子線程之分。
Dart 也是 Event-Loop 以及 Event - Queue 的模型,全部的事件都是經過 Event-loop 的依次執行。
而 Dart 的 Event Loop 就是:
從 EventQueue 中獲取 Event
處理 Event
直到 EventQueue 爲空
而這些 Event 包括了用戶輸入,點擊, Timer, 文件 IO 等
一旦某個 Dart 的函數開始執行,它將執行到這個函數結束,也就是 Dart 的函數不會被其餘 Dart 代碼打斷。
Dart 中沒有線程的概念,只有 isolate(隔離),每一個 isolate 都是隔離的,並不會共享內存。而一個 Dart 程序是在 Main isolate 的 main 函數開始,而在 Main 函數結束後,Main isolate 線程開始一個一個(one by one)的開始處理 Event Queue 中的每個 Event 。
Dart 中的 Main Isolate 只有一個 Event - Looper,可是存在兩個 Event Queue : Event Queue 以及 Microtask Queue Microtask Queue 存在的意義是: 但願經過這個 Queue 來處理稍晚一些的事情,可是在下一個消息到來以前須要處理完的事情。 當 Event Looper 正在處理 Microtask Queue 中的 Event 時候,Event Queue 中的 Event 就中止了處理了,此時 App 不能繪製任何圖形,不能處理任何鼠標點擊,不能處理文件 IO 等等。
Event-Looper 挑選 Task 的執行順序爲:
優先所有執行完 Microtask Queue.
直到 Microtask Queue 爲空時,纔會執行 Event Queue 中的 Event.
Dart 中只能知道 Event 處理的前後順序,可是並不知道某個 Event 執行的具體時間點,由於它的處理模型是一個單線程循環,而不是基於時鐘調度(即它的執行只是按照 Event 處理完,就開始循環下一個 Event,而與Java 中的 Thread 調度不同,沒有時間調度的概念),也就是咱們既是指定另外一個 Delay Time 的 Task,但願它在預期的時間後開始執行,它有可能不會在那個時間執行,須要看是否前面的 Event 是否已經 Dequeue 。
當有代碼能夠在後續任務執行的時候,有兩種方式,經過dart:async這個Lib中的API便可:
當使用 EventQueue 時,須要考慮清楚,儘可能避免 microtask queue 過於龐大,不然會阻塞其餘事件的處理
先來看一段代碼
void testFuture() {
Future f = new Future(() => print("1"));
Future f1 = new Future(() => null);
Future f2 = new Future(() => null);
Future f3 = new Future(() => null);
f3.then((_) => print("2"));
f2.then((_) {
print("3");
new Future(() => print("4"));
f1.then((_) {
print("5");
});
});
f1.then((m) {
print("6");
});
print("7");
}
複製代碼
你們來猜一猜上面的打印效果,打印結果是:
7
1
6
3
5
2
4
複製代碼
是否是沒有想到。下面咱們來分析下執行流程
注意:這裏的 f,f1,f2,f3... 進入事件隊列裏面要把它們理解成是一個總體,無論其它地方 還有 f1.then 或者 f2.then 。最後都是一個總體。
注意
使用 new Future 將任務加入 event 隊列。
Future 中的 then 並無建立新的 Event 丟到 Event Queue 中,而只是一個普通的 Function Call ,
FutureTask 執行完後,當即開始執行。
若是在 then() 調用以前 Future 就已經執行完畢了,那麼任務會被加入到 microtask 隊列中,而且該任務會執行 then() 中註冊的回調函數。
使用 Future.value 構造函數的時候,就會上一條同樣,建立 Task 丟到 microtask Queue 中執行 then 傳入的函數。
Future.sync 構造函數執行了它傳入的函數以後,也會當即建立 Task 丟到 microtask Queue 中執行。
當任務須要延遲執行時,可使用 new Future.delay() 來將任務延遲執行。
看一段代碼,而後再來分析它們的執行流程
//scheduleMicrotask
void testScheduleMicrotask() {
//918346572
scheduleMicrotask(() => print('s1'));
new Future.delayed(new Duration(seconds: 1), () => print('s2'));
new Future(() => print('s3')).then((_) {
print('s4');
scheduleMicrotask(() => print('s5'));
}).then((_) => print('s6'));
new Future(() => print('s10'))
.then((_) => new Future(() => print('s11')))
.then((_) => print('s12'));
new Future(() => print('s7'));
scheduleMicrotask(() => print('s8'));
print('s9');
}
複製代碼
打印結果
s9
s1
s8
s3
s4
s6
s5
s10
s7
s11
s12
s2
複製代碼
這一次你們本身分析了 咳咳 ~ 。
注意
//同步生成器
//調用getSyncGenerator 馬上返回 Iterable
void main() {
var it = getSyncGenerator(5).iterator;
// 調用moveNext方法時getSyncGenerator纔開始執行
while (it.moveNext()) {
print(it.current);
}
}
//同步生成器: 使用sync*,返回的是Iterable對象
Iterable<int> getSyncGenerator(int n) sync* {
print('start');
int k = n;
while (k > 0) {
//yield會返回moveNext爲true,並等待 moveNext 指令
yield k--;
}
print('end');
}
複製代碼
注意
//異步生成器調用
// getAsyncGenerator當即返回Stream,只有執行了listen,函數纔會開始執行
StreamSubscription subscription = getAsyncGenerator(5).listen(null);
subscription.onData((value) {
print(value);
if (value >= 2) {
subscription.pause(); //可使用StreamSubscription對象對數據流進行控制
}
});
//異步生成器: 使用async*,返回的是Stream對象
Stream<int> getAsyncGenerator(int n) async* {
print('start');
int k = 0;
while (k < n) {
//yield不用暫停,數據以流的方式一次性推送,經過StreamSubscription進行控制
yield k++;
}
print('end');
}
複製代碼
注意
void main(){
//異步
getAsyncRecursiveGenerator(5).listen((value) => print(value));
}
//異步遞歸生成器
Stream<int> getAsyncRecursiveGenerator(int n) async* {
if (n > 0) {
yield n;
yield* getAsyncRecursiveGenerator(n - 1);
}
}
複製代碼
void main (){
//遞歸生成器
//同步
var it1 = getSyncRecursiveGenerator(5).iterator;
while (it1.moveNext()) {
print(it1.current);
}
}
//遞歸生成器:使用yield*
Iterable<int> getSyncRecursiveGenerator(int n) sync* {
if (n > 0) {
yield n;
yield* getSyncRecursiveGenerator(n - 1);
}
}
複製代碼
注意
現代的瀏覽器以及移動瀏覽器都運行在多核 CPU 系統上。 要充分利用這些 CPU,開發者通常使用共享內存 數據來保證多線程的正確執行。然而, 多線程共享數據一般會致使不少潛在的問題,並致使代碼運行出錯。
全部的 Dart 代碼在 isolates 中運行而不是線程。 每一個 isolate 都有本身的堆內存,而且確保每一個 isolate 的狀態都不能被其餘 isolate 訪問。
main() {
dynamic tv = new Television();
tv.activate();
tv.turnOn();
}
class Television {
@deprecated
void activate() {
turnOn();
}
void turnOn() {
print('Television turn on!');
}
}
複製代碼
main() {
dynamic tv = new Television();
tv.activate();
tv.turnOn();
tv.turnOff();
}
class Television {
@deprecated
void activate() {
turnOn();
}
void turnOn() {
print('Television turn on!');
}
@override
noSuchMethod(Invocation mirror) {
print('沒有找到方法');
}
}
複製代碼
注意
建立 todo.dart 文件
//todo.dart
class Todo {
final String who;
final String what;
const Todo({this.who, this.what});
}
複製代碼
import 'todo.dart’; main() { dynamic tv = new Television(); tv.doSomething(); } class Television { @Todo(who: 'damon', what: 'create a new method') void doSomething() { print('doSomething'); } } 複製代碼
注意
// 跟 Java 同樣
複製代碼
/**跟 Java 同樣**/
複製代碼
/// 跟 Java 同樣
複製代碼
這 三篇基礎 內容介紹了常見的 Dart 語言特性。 還有更多特性有待實現,可是新的特性不會破壞已有的代碼。 更多信息請參考 Dart 語言規範 和 Effective Dart。
要了解 Dart 核心庫的詳情,請參考 Dart 核心庫預覽。