<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
今天介紹一下 Dart 學習的最後一節內容,包括非同步的使用、生成器語法以及型別別名的使用。
Dart 類庫有非常多的返回Future
或者Stream
物件的函數。 這些函數被稱為非同步函數:它們只會在設定好一些耗時操作之後返回,比如像 IO 操作。而不是等到這個操作完成。
同時,async
和await
關鍵詞支援了非同步程式設計,允許您寫出和同步程式碼很像的非同步程式碼。
Future
與 JavaScript 中的Promise
非常相似,表示一個非同步操作的最終完成(或失敗)及其結果值的表示。簡單來說,它就是用於處理非同步操作的,非同步處理成功了就執行成功的操作,非同步處理失敗了就捕獲錯誤或者停止後續操作。一個Future 只會對應一個結果,要麼成功,要麼失敗。
注: Future
的所有API的返回值仍然是一個Future
物件,所以可以很方便的進行鏈式呼叫。
為了方便範例,在本例中使用Future.delayed
建立了一個延時任務(實際場景會是一個真正的耗時任務,比如一次網路請求),即2秒後返回結果字串hi world!
,然後我們在then
中接收非同步結果並列印結果,程式碼如下:
Future.delayed(new Duration(seconds: 2),(){ return "hi world!"; }).then((data){ print(data); });
如果非同步任務發生錯誤,可以在catchError
中捕獲錯誤,將上面範例改為:
Future.delayed(new Duration(seconds: 2),(){ //return "hi world!"; throw AssertionError("Error"); }).then((data){ //執行成功會走到這裡 print("success"); }).catchError((e){ //執行失敗會走到這裡 print(e); });
then
方法還有一個可選引數onError
,也可以它來捕獲異常:
Future.delayed(new Duration(seconds: 2), () { //return "hi world!"; throw AssertionError("Error"); }).then((data) { print("success"); }, onError: (e) { print(e); });
有些時候,我們會遇到無論非同步任務執行成功或失敗都需要做一些事的場景,比如在網路請求前彈出載入對話方塊,在請求結束後關閉對話方塊。這種場景,有兩種方法,第一種是分別在then
或catch
中關閉一下對話方塊,第二種就是使用Future
的whenComplete
回撥,我們將上面範例改一下:
Future.delayed(new Duration(seconds: 2),(){ //return "hi world!"; throw AssertionError("Error"); }).then((data){ //執行成功會走到這裡 print(data); }).catchError((e){ //執行失敗會走到這裡 print(e); }).whenComplete((){ //無論成功或失敗都會走到這裡 });
使用Future.wait
可以做到多個Future
同時出發才會進行後續操作,同 JavaScript 中的Promise.all()
方法。
Future.wait
接受一個Future
陣列引數,只有陣列中所有Future
都執行成功後,才會觸發then
的成功回撥,只要有一個Future
執行失敗,就會觸發錯誤回撥。下面,我們通過模擬Future.delayed
來模擬兩個資料獲取的非同步任務,等兩個非同步任務都執行成功時,將兩個非同步任務的結果拼接列印出來,程式碼如下:
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
修飾符標記的函數。 向函數中新增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); // 執行接下來的操作 } catch(e){ // 錯誤處理 print(e); } }
async
用來表示函數是非同步的,定義的函數會返回一個Future
物件,可以使用then方法新增回撥函數。await
後面是一個Future
,表示等待該非同步任務完成,非同步完成後才會往下走。注意,await
必須出現在 async
函數內部。注意: 函數的主體不需要使用 Future 的 API。如果需要,Dart 將建立 Future
的物件。如果沒有返回一個有用的值,那麼將其返回Future<void>
型別。
Stream
也是用於接收非同步事件資料,和Future
不同的是,它可以接收多個非同步操作的結果(成功或失敗)。 也就是說,在執行非同步任務時,可以通過多次觸發成功或失敗事件來傳遞結果資料或錯誤異常。 Stream
常用於會多次讀取資料的非同步任務場景,如網路內容下載、檔案讀寫等。
當需要從 Stream 獲取值時,有兩個選擇:
使用async
和非同步的for
迴圈(await for
)
注: 在使用await for
之前,請確保它使程式碼更清晰,並且確實希望等待流(Stream)的所有結果。例如,通常不應該為 UI 事件使用await
,因為 UI 框架會傳送無窮無盡的事件流。
非同步for
迴圈有以下形式:
await for (varOrType identifier in expression) { // Executes each time the stream emits a value. }
表示式的值必須具有 Stream 型別。執行過程如下:
等待流發出值。
執行for
迴圈的主體,並將變數設定為發出的值。
重複1和2,直到流關閉。
要停止偵聽流,可以使用break
或return
語句,該語句將跳出for
迴圈,並從流中取消訂閱。
如果在實現非同步for
迴圈時出現編譯時錯誤,請確保await
在非同步函數中。例如,要在應用程式的main()
函數中使用非同步for
迴圈,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 內建支援兩種生成器函數:
要實現同步生成器函數,將函數體標記為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
。如果我們更改程式碼以使用顯式名稱和保留型別資訊,開發人員和工具都可以使用這些資訊。
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
以上就是Dart 非同步程式設計生成器及自定義型別用法詳解的詳細內容,更多關於Dart 非同步程式設計生成器的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45