Dart 代碼庫中有大量返回 Future 或 Stream 對(duì)象的函數(shù)楞艾,這些函數(shù)都是 異步 的,它們會(huì)在耗時(shí)操作(比如I/O)執(zhí)行完畢前直接返回而不會(huì)等待耗時(shí)操作執(zhí)行完畢壳炎。
async
和 await
關(guān)鍵字用于實(shí)現(xiàn)異步編程拢操,并且讓你的代碼看起來就像是同步的一樣。
處理 Future
可以通過下面兩種方式抽诉,獲得 Future 執(zhí)行完成的結(jié)果:
- 用 async 和 await。
- 使用 Future API吐绵,具體描述迹淌,參考庫概覽。
使用 async
和await
的代碼是異步的己单,但是看起來有點(diǎn)像同步代碼唉窃。例如,下面的代碼使用 await
等待異步函數(shù)的執(zhí)行結(jié)果纹笼。
await lookUpVersion();
await
必須在帶有async
關(guān)鍵字的 異步函數(shù) 中使用:
Future checkVersion() async {
var version = await lookUpVersion();
// 使用 version 繼續(xù)處理邏輯
}
備忘??:盡管異步函數(shù)可以處理耗時(shí)操作纹份,但是它并不會(huì)等待這些耗時(shí)操作完成,異步函數(shù)執(zhí)行時(shí)會(huì)在其遇到第一個(gè) await
表達(dá)式(詳情見)的時(shí)候返回一個(gè) Future 對(duì)象,然后等待 await
表達(dá)式執(zhí)行完畢后繼續(xù)執(zhí)行蔓涧。
使用 try
件已、catch
以及finally
來處理使用 await
導(dǎo)致的異常:
try {
version = await lookUpVersion();
} catch (e) {
// 無法找到版本時(shí)做出的反應(yīng)
}
你可以在異步函數(shù)中多次使用 await
關(guān)鍵字。例如元暴,下面代碼中等待了三次函數(shù)結(jié)果:
var entrypoint = await findEntrypoint();
var exitCode = await runExecutable(entrypoint, args);
await flushThenExit(exitCode);
await
表達(dá)式的返回值通常是一個(gè) Future
對(duì)象篷扩;如果不是的話也會(huì)自動(dòng)將其包裹在一個(gè)Future
對(duì)象里。Future
對(duì)象代表一個(gè)“承諾”茉盏,await
表達(dá)式會(huì)阻塞直到需要的對(duì)象返回鉴未。
如果在使用 await 時(shí)導(dǎo)致編譯錯(cuò)誤,請(qǐng)確保 await 在一個(gè)異步函數(shù)中使用鸠姨。例如铜秆,如果想在 main() 函數(shù)中使用 await,那么 main() 函數(shù)就必須使用 async 關(guān)鍵字標(biāo)識(shí)讶迁。
Future main() async {
checkVersion();
print('在 Main 函數(shù)中執(zhí)行:版本是 ${await lookUpVersion()}');
}
聲明異步函數(shù)
定義 異步函數(shù) 只需在普通方法上加上 async
關(guān)鍵字即可连茧。
將關(guān)鍵字async
添加到函數(shù)并讓其返回一個(gè) Future
對(duì)象。假設(shè)有如下返回 String 對(duì)象的方法:
String lookUpVersion() => '1.0.0';
將其改為異步函數(shù)巍糯,返回值是 Future:
Future<String> lookUpVersion() async => '1.0.0';
注意梅屉,函數(shù)體不需要使用 Future API。如有必要鳞贷,Dart 會(huì)創(chuàng)建 Future 對(duì)象坯汤。
如果函數(shù)沒有返回有效值夭苗,需要設(shè)置其返回類型為 Future<void>
爆安。
關(guān)于 futures、async
和 await
的使用介紹易遣,可以參見這個(gè) codelab: asynchronous programming codelab咱筛。
處理 Stream
如果想從 Stream 中獲取值搓幌,可以有兩種選擇:
- 使用 async 關(guān)鍵字和一個(gè) 異步循環(huán)(使用 await for 關(guān)鍵字標(biāo)識(shí))。
- 使用 Stream API迅箩。詳情參考庫概覽溉愁。
在使用 await for 關(guān)鍵字前,確保其可以令代碼邏輯更加清晰并且是真的需要等待所有的結(jié)果執(zhí)行完畢饲趋。例如拐揭,通常不應(yīng)該在 UI 事件監(jiān)聽器上使用 await for 關(guān)鍵字,因?yàn)?UI 框架發(fā)出的事件流是無窮盡的奕塑。
使用 await for 定義異步循環(huán)看起來是這樣的:
await for (varOrType identifier in expression) {
// 每當(dāng) Stream 發(fā)出一個(gè)值時(shí)會(huì)執(zhí)行
}
表達(dá)式 的類型必須是 Stream堂污。執(zhí)行流程如下:
1 等待直到 Stream 返回一個(gè)數(shù)據(jù)。
2 使用 1 中 Stream 返回的數(shù)據(jù)執(zhí)行循環(huán)體龄砰。
3 重復(fù) 1盟猖、2 過程直到 Stream 數(shù)據(jù)返回完畢讨衣。
使用 break 和 return 語句可以停止接收 Stream 數(shù)據(jù),這樣就跳出了循環(huán)并取消注冊(cè)監(jiān)聽 Stream式镐。
如果在實(shí)現(xiàn)異步 for 循環(huán)時(shí)遇到編譯時(shí)錯(cuò)誤反镇,請(qǐng)檢查確保 await for 處于異步函數(shù)中。 例如娘汞,要在應(yīng)用程序的 main() 函數(shù)中使用異步 for 循環(huán)歹茶,main() 函數(shù)體必須標(biāo)記為 async:
Future main() async {
// ...
await for (var request in requestServer) {
handleRequest(request);
}
// ...
}
你可以查閱庫概覽中有關(guān) dart:async 的部分獲取更多有關(guān)異步編程的信息。