小菜前幾天剛學習了 Future 實現非同步操作的部分方法,主要包括構造方法和常用的靜態方法;小菜今天繼續學習 Future 其他知識和 async-await 方式實現非同步操作;
Future 巢狀
小菜在上篇部落格中未做 Future 巢狀嘗試,有很多場景需要多個非同步處理,且每個非同步都需要上個非同步返回的結果 then() 之後才可以繼續,此時可以用 Future 巢狀方式;但如果潛套方法較多可能會對今後的程式碼維護造成一定影響,即常說到的 Callback hell;
_futureMoreThen() {
Future.delayed(Duration(seconds: 3), () {
return 'Future.delayed 3s!';
}).then((val) {
print('Future -> then(1) -> $val');
return 'then 1';
}).then((val) {
print('Future -> then(2) -> $val');
return 'then 2';
}).then((val) {
print('Future -> then(3) -> $val');
return 'then 3';
}).whenComplete(() => print('Future whenComplete!'));
}
複製程式碼
_futureMoreThen02() {
Future.delayed(Duration(seconds: 3), () {
return 'Future.delayed 3s!';
}).then((val) {
print('Future -> then(1) -> $val');
return Future.delayed(Duration(seconds: 2), () {
return 'Future.delayed 2s!';
}).then((val) {
print('Future -> then(2) -> $val');
return Future.delayed(Duration(seconds: 2), () {
return 'Future.delayed 2s!';
}).then((val) {
print('Future -> then(3) -> $val');
});
});
}).whenComplete(() => print('Future whenComplete!'));
}
複製程式碼
async-await
Future 也可以通過 async-await 實現非同步操作;其使用場景通常是在多個 Future 串聯起來,多層級巢狀而導致的 Callback hell,使用 async-await 實現非同步;
async
async 用來修飾的非同步方法最終將返回值封裝成 Future 物件;
await
await 會把自動把該方法進入阻塞狀態,一直待任務執行完成並返回對應值;
案例嘗試
小菜先嚐試了基本的 async-await 用法;
- 小菜未採用 async 和 await 關鍵詞,此時 Future.delayed() 返回的是一個 Future 物件,不能同步的獲取返回資料;
print(_function01());
_function01() {
var result = Future.delayed(Duration(seconds: 3), () {
return 'Future.delayed 3s!';
}).then((val) => print('Function01 -> then() -> $val'))
.whenComplete(() => print('Function01 -> whenComplete!'));
return 'Function01 -> $result';
}
複製程式碼
- 小菜僅新增了 async 關鍵詞,將該方法修飾為非同步方法,依舊不能直接獲取返回資料
print(_function01());
Future<String> _function02() async {
var result = Future.delayed(Duration(seconds: 3), () {
return 'Future.delayed 3s!';
}).then((val) => print('Function02 -> then() -> $val'))
.whenComplete(() => print('Function02 -> whenComplete!'));
return 'Function02 -> $result';
}
複製程式碼
- 小菜新增了 async 和 await 兩個關鍵詞,編譯器最終會將其轉化為一個 Promise(Future) 的呼叫鏈,可以待非同步完成之後獲取返回結果;此時 Future 不能設定 then() 回撥方法等;
print(await _function03());
_function03() async {
var result = await Future.delayed(Duration(seconds: 3), () {
return 'Future.delayed 3s!';
});
return 'Function03 -> $result';
}
複製程式碼
- 小菜嘗試只用 await,此時提示 The await expression can only used in an async function,await 只能用在 async 方法內部;
- 採用 async-await 方式時,對於異常的捕獲,可以通過 Future.catchError() 來處理,還可以採用最常用的 try-catch-finally 方式,小菜簡單理解對應 then()-catchError()-whenComplete();
await _function04();
_function04(index) async {
switch (index) {
case 1:
await Future.error(ArgumentError.notNull('Input')).catchError((val) => print('Function04 -> $val'));
break;
case 2:
try {
await Future.error(ArgumentError.notNull('Input'));
} catch (e) {
print('Function04 -> catch -> $e');
} finally {
print('Function04 -> Finally!');
}
break;
}
}
複製程式碼
- 針對多個 Future 巢狀導致的 Callback hell,async-await 處理方式要簡潔一些;
await _functionThen();
_functionThen() async {
await _itemThen01();
}
_itemThen01() async {
await Future.delayed(Duration(seconds: 3), () {
print('Future -> then(1) -> Future.delayed 3s!');
return _itemThen02();
});
}
_itemThen02() async {
await Future.delayed(Duration(seconds: 2), () {
print('Future -> then(2) -> Future.delayed 2s!');
return _itemThen03();
});
}
_itemThen03() async {
await Future.delayed(Duration(seconds: 2), () {
print('Future -> then(3) -> Future.delayed 2s!');
return 'Future.delayed 2s!';
}).whenComplete(() => print('Future whenComplete!'));
}
複製程式碼
小擴充套件
小菜在嘗試 async-await 時還遇到 async*,小菜在 bloc 狀態管理時使用時都是 async* 和 Stream,小菜簡單瞭解一下相關差異;
async*
async* 也可以用於非同步,方法前使用 async* 關鍵字可以將該方法標記為非同步生成器,返回的是一個 Stream 物件,使用 yield 語句來傳遞值;
對於 Stream 的使用,小菜之前有基本的瞭解,一般通過 skin 新增資料,通過 listen 進行資料監聽;
yield 關鍵字會向 async* 宣告的一步生成器的輸出流新增一個值,有點類似 return,但不會終止函式;
_function06() async* {
for (int i = 1; i <= 10; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}
複製程式碼
await for (int num in _function06()) {
print('current num = $num');
}
複製程式碼
_function06().listen((num) => print('current num = $num'));
複製程式碼
小菜對 Dart 非同步的認知還不完全,接下來會繼續嘗試 isolate 以及 EventLoop 執行順序等;如有錯誤和遺漏請多多指導!
來源: 阿策小和尚