網站首頁 編程語言 正文
異步支持
今天介紹一下 Dart 學習的最后一節(jié)內容,包括異步的使用、生成器語法以及類型別名的使用。
Dart 類庫有非常多的返回Future
或者Stream
對象的函數。 這些函數被稱為異步函數:它們只會在設置好一些耗時操作之后返回,比如像 IO 操作。而不是等到這個操作完成。
同時,async
和await
關鍵詞支持了異步編程,允許您寫出和同步代碼很像的異步代碼。
Future
Future
與 JavaScript 中的Promise
非常相似,表示一個異步操作的最終完成(或失敗)及其結果值的表示。簡單來說,它就是用于處理異步操作的,異步處理成功了就執(zhí)行成功的操作,異步處理失敗了就捕獲錯誤或者停止后續(xù)操作。一個Future 只會對應一個結果,要么成功,要么失敗。
注: Future
的所有API的返回值仍然是一個Future
對象,所以可以很方便的進行鏈式調用。
Future.then
為了方便示例,在本例中使用Future.delayed
創(chuàng)建了一個延時任務(實際場景會是一個真正的耗時任務,比如一次網絡請求),即2秒后返回結果字符串hi world!
,然后我們在then
中接收異步結果并打印結果,代碼如下:
Future.delayed(new Duration(seconds: 2),(){ return "hi world!"; }).then((data){ print(data); });
Future.catchError
如果異步任務發(fā)生錯誤,可以在catchError
中捕獲錯誤,將上面示例改為:
Future.delayed(new Duration(seconds: 2),(){ //return "hi world!"; throw AssertionError("Error"); }).then((data){ //執(zhí)行成功會走到這里 print("success"); }).catchError((e){ //執(zhí)行失敗會走到這里 print(e); });
then
方法還有一個可選參數onError
,也可以它來捕獲異常:
Future.delayed(new Duration(seconds: 2), () { //return "hi world!"; throw AssertionError("Error"); }).then((data) { print("success"); }, onError: (e) { print(e); });
Future.whenComplete
有些時候,我們會遇到無論異步任務執(zhí)行成功或失敗都需要做一些事的場景,比如在網絡請求前彈出加載對話框,在請求結束后關閉對話框。這種場景,有兩種方法,第一種是分別在then
或catch
中關閉一下對話框,第二種就是使用Future
的whenComplete
回調,我們將上面示例改一下:
Future.delayed(new Duration(seconds: 2),(){ //return "hi world!"; throw AssertionError("Error"); }).then((data){ //執(zhí)行成功會走到這里 print(data); }).catchError((e){ //執(zhí)行失敗會走到這里 print(e); }).whenComplete((){ //無論成功或失敗都會走到這里 });
Future.wait
使用Future.wait
可以做到多個Future
同時出發(fā)才會進行后續(xù)操作,同 JavaScript 中的Promise.all()
方法。
Future.wait
接受一個Future
數組參數,只有數組中所有Future
都執(zhí)行成功后,才會觸發(fā)then
的成功回調,只要有一個Future
執(zhí)行失敗,就會觸發(fā)錯誤回調。下面,我們通過模擬Future.delayed
來模擬兩個數據獲取的異步任務,等兩個異步任務都執(zhí)行成功時,將兩個異步任務的結果拼接打印出來,代碼如下:
Future.wait([ // 2秒后返回結果 Future.delayed(new Duration(seconds: 2), () { return "hello"; }), // 4秒后返回結果 Future.delayed(new Duration(seconds: 4), () { return " world"; }) ]).then((results){ print(results[0]+results[1]); }).catchError((e){ print(e); }); /* 最后會在4秒后拿到結果 */
更多 Future 的 api 請自行查詢文檔。
async 和 await
異步函數是函數體被用async
修飾符標記的函數。 向函數中添加async
關鍵字將使其返回一個 Future。
String lookUpVersion() => '1.0.0'; // 返回String
Future<String> lookUpVersion() async => '1.0.0'; // 返回Future<String>
然后我們可以使用await
關鍵字在內部直接接受一個 Future 的then
的成功回調,就如同 JavaScript 中的那樣:
task() async { try{ String id = await login("alice","******"); String userInfo = await getUserInfo(id); await saveUserInfo(userInfo); // 執(zhí)行接下來的操作 } catch(e){ // 錯誤處理 print(e); } }
-
async
用來表示函數是異步的,定義的函數會返回一個Future
對象,可以使用then方法添加回調函數。 -
await
后面是一個Future
,表示等待該異步任務完成,異步完成后才會往下走。注意,await
必須出現在async
函數內部。
注意: 函數的主體不需要使用 Future 的 API。如果需要,Dart 將創(chuàng)建 Future
的對象。如果沒有返回一個有用的值,那么將其返回Future<void>
類型。
處理流(Stream)
Stream
也是用于接收異步事件數據,和Future
不同的是,它可以接收多個異步操作的結果(成功或失敗)。 也就是說,在執(zhí)行異步任務時,可以通過多次觸發(fā)成功或失敗事件來傳遞結果數據或錯誤異常。 Stream
常用于會多次讀取數據的異步任務場景,如網絡內容下載、文件讀寫等。
當需要從 Stream 獲取值時,有兩個選擇:
使用async
和異步的for
循環(huán)(await for
)
注: 在使用await for
之前,請確保它使代碼更清晰,并且確實希望等待流(Stream)的所有結果。例如,通常不應該為 UI 事件使用await
,因為 UI 框架會發(fā)送無窮無盡的事件流。
異步for
循環(huán)有以下形式:
await for (varOrType identifier in expression) { // Executes each time the stream emits a value. }
表達式的值必須具有 Stream 類型。執(zhí)行過程如下:
等待流發(fā)出值。
執(zhí)行for
循環(huán)的主體,并將變量設置為發(fā)出的值。
重復1和2,直到流關閉。
要停止偵聽流,可以使用break
或return
語句,該語句將跳出for
循環(huán),并從流中取消訂閱。
如果在實現異步for
循環(huán)時出現編譯時錯誤,請確保await
在異步函數中。例如,要在應用程序的main()
函數中使用異步for
循環(huán),main()
的主體必須標記為async
:
Future main() async { // ... await for (var request in requestServer) { handleRequest(request); } // ... }
使用Stream API,如[庫的引導]中的描述
Stream.fromFutures([ // 1秒后返回結果 Future.delayed(new Duration(seconds: 1), () { return "hello 1"; }), // 拋出一個異常 Future.delayed(new Duration(seconds: 2),(){ throw AssertionError("Error"); }), // 3秒后返回結果 Future.delayed(new Duration(seconds: 3), () { return "hello 3"; }) ]).listen((data){ print(data); }, onError: (e){ print(e.message); }, onDone: (){ }); /* 上面的代碼依次會輸出: I/flutter (17666): hello 1 I/flutter (17666): Error I/flutter (17666): hello 3 */
生成器
當需要延遲地生成一個值序列時,請考慮使用生成器函數。
Dart 內置支持兩種生成器函數:
- 同步生成器:返回 Iterable 對象
- 異步生成器:返回 Stream 對象
要實現同步生成器函數,將函數體標記為sync*
,并使用yield
語句傳遞值:
Iterable<int> naturalsTo(int n) sync* { int k = 0; while (k < n) yield k++; }
要實現異步生成器函數,將函數體標記為async*
,并使用yield
語句傳遞值:
Stream<int> asynchronousNaturalsTo(int n) async* { int k = 0; while (k < n) yield k++; }
如果生成器是遞歸的,可以使用yield*
來改進它的性能:
Iterable<int> naturalsDownFrom(int n) sync* { if (n > 0) { yield n; yield* naturalsDownFrom(n - 1); } }
類型定義
在 Dart 中,函數是對象,就像字符串和數字是對象一樣。typedef
或function-type
為函數提供一個類型別名,可以在聲明字段和返回類型時使用這個名稱。當函數類型被分配給變量時,typedef
保留類型信息。
以下代碼不使用typedef
:
class SortedCollection { Function compare; SortedCollection(int f(Object a, Object b)) { compare = f; } } // Initial, broken implementation. int sort(Object a, Object b) => 0; void main() { SortedCollection coll = SortedCollection(sort); // All we know is that compare is a function, // but what type of function? assert(coll.compare is Function); }
上面的代碼中,當給compare
分配f時類型信息會丟失。f
的類型是(Object, Object)->int(int表示返回值類型)
,當然,compare
的類型是Function
。如果我們更改代碼以使用顯式名稱和保留類型信息,開發(fā)人員和工具都可以使用這些信息。
typedef Compare = int Function(Object a, Object b); class SortedCollection { Compare compare; SortedCollection(this.compare); } // Initial, broken implementation. int sort(Object a, Object b) => 0; void main() { SortedCollection coll = SortedCollection(sort); assert(coll.compare is Function); assert(coll.compare is Compare); }
注意: 目前,typedefs
僅限于函數類型,可能在之后會有所改變。
因為typedef
僅僅是別名,所以它們提供了一種檢查任何函數類型的方法。例如:
typedef Compare<T> = int Function(T a, T b); int sort(int a, int b) => a - b; void main() { assert(sort is Compare<int>); // True! }
元數據
使用元數據提供關于代碼的附加信息。元數據注釋以字符@
開頭,后跟對編譯時常量(如deprecated
)的引用或對常量構造函數的調用。
所有 dart 代碼都可以使用兩個注釋:@deprecated
(棄用注釋)和@override
。這里有一個使用@deprecated
注釋的例子:
class Television { /// _Deprecated: Use [turnOn] instead._ @deprecated void activate() { turnOn(); } /// Turns the TV's power on. void turnOn() {...} }
可以定義自己的元數據注釋(也就是類似 JavaScript 中的裝飾器的效果)。這里有一個定義帶有兩個參數的@todo注釋的示例:
library todo; class Todo { final String who; final String what; const Todo(this.who, this.what); }
這里有一個使用@todo
注釋的例子:
import 'todo.dart'; @Todo('seth', 'make this do something') void doSomething() { print('do something'); }
元數據可以出現在庫、類、類型定義、類型參數、構造函數、工廠、函數、字段、參數或變量聲明之前,也可以出現在導入或導出指令之前。可以使用反射在運行時檢索元數據。
核心庫的使用
可以參考官方文檔中的介紹:A tour of the core libraries
原文鏈接:https://juejin.cn/post/7160554327395237896
相關推薦
- 2022-07-01 python神經網絡Batch?Normalization底層原理詳解_python
- 2022-09-05 淺析Servlet三大容器的常用方法及其作用域
- 2022-06-15 ASP.NET?MVC使用區(qū)域(Area)功能_基礎應用
- 2022-07-01 .NET設計模式之UML類圖介紹_基礎應用
- 2022-09-08 C++實現Dijkstra算法的示例代碼_C 語言
- 2022-02-09 C++解決輸出鏈表中倒數k個結點的問題_C 語言
- 2022-06-04 C#?XML基礎入門小結(XML文件內容增刪改查清)_C#教程
- 2022-10-21 React實現動態(tài)調用的彈框組件_React
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支