【google教程】async/await的使用和原理

文章受權首發於code4flutter,如需轉載請註明來源 code4flutter.com/async-await…

本文是flutter油管教程第3篇,介紹async和await的使用和原理html

引言

不少語言使用async和await做爲語法,將函數變成async,而後在代碼的某個位置,它會進行等待,直到任務處理完成返回。在flutter中也是這樣的基本功能,可是須要特別指出的是這種語法糖只是feature和stream的封裝,可讓你更清晰的更易讀的代碼。api

對比

code4flutter-async/await

這裏是一個處理數據處理的例子,網絡請求和文件IO操做是異步操做bash

咱們想讓他們放在一塊兒工做,首先經過網絡請求建立一個id,根據這個id進行網絡通訊,而後用結果建立一個processdata的對象,使用dart的feature的api,而後將回調連接在一塊兒。這樣用前一個的回調做爲下一個回調的參數輸入,以此類推。可是以前的方式並不具備很好的可讀性,須要層層嵌套,可是用feature結合async和wait不同。網絡

ProcessedData createData() async{
   final id = _loadFromDisk();
   final data = _fetchNetworkData(id);
   return ProcessedData(data);
}複製代碼

首先,添加async關鍵字,只是告訴編譯器,「嘿,我打算在這裏使用await關鍵字」app

接下來咱們來添加await關鍵字,在每一個feature以前,標誌須要等待的函數,異步

最後,在返回值添加feature關鍵字,有人以爲很奇怪,爲何只是普通的返回處理數據結果的函數須要feature修飾呢,這是由於在processData以前有兩次異步操做用到了await。這意味着他開始執行,而後中止,等待磁盤事件,而後繼續,而後停下來等待網絡事件。只有在那以後才能提供一個值。代碼以下:async

ProcessedData createData() async{
   final id = await _loadFromDisk();
   final data = await _fetchNetworkData(id);
   return ProcessedData(data);
}複製代碼

asyc/await 工做原理

因此當createData開始運行並觸發第一個await時,他纔會返回一個feature,到調用函數,他說,「嘿,我須要等待一下東西」,這是一個空盒子,你須要等待一下子,當我在網絡中等待的磁盤數據到達時,我將調用return語句併爲你提供一些數據,把它放入futurebuilder,或其餘東西中,這就是他的工做原理。用代碼翻譯以下:ide

Future<ProcessedData> createData(){
   return _loadFromDisk().then((id){
     return _fetchNetworkData(id);
}).then((data){
    return ProcessData(data);
})
}複製代碼

用feature api的好處就是你能夠清楚的看到代碼是如何將事件進行拆分的。首先函數開始執行,調用loadfromdisk,等待來來自磁盤的數據觸發事件循環,而後第二個回調被調用,請求網絡,等待來自網絡的數據到達事件循環,而後處理數據。此時調用return,此時該值完成了。函數

如今咱們使用async/await版本實現。經過await表達式做爲等待。由此能夠獲得如下代碼。fetch

async/await正確姿式

Future<ProcessedData>createData() async{
   final id = await _loadFromDisk();
   final data = await _fetchNetworkData(id);
   return ProcessedData(data);
}複製代碼

createData開始執行,並觸發第一個等待,此時它將本身的future返回給函數,並調用loadFromDisk,而後像以前同樣,等待來自磁盤文件I/O事件,這樣就完成了由loadFromDisk返回的future,這意味着createData結束了等待,並能夠繼續執行其他的代碼了。再次等待,請求網絡數據等待返回,最終網絡數據到達事件循環,這就完成了由fetchNetworkData返回的future。createData就能夠再次繼續運行了,他建立並返回了一個ProcessedData的實例。他完成了createData在開頭處傳給調用者的那個future。

由此能夠看到,相同的事件循環控制着代碼的運行,並涉及到相同的future,惟一變化的是,有了async/await,函數代碼變少了,看起來更像是同步的代碼。

async/await如何處理錯誤

普通的寫法,處理錯誤以下,

code4flutter-async/await

使用catcherror來處理錯誤,完成後悔在最後執行回調,不管是否有錯誤發生。

在aync/await的方式中不用任何回調,而是用try catch

code4flutter-async/await

可使用try catch捕獲特定類型的異常,finally將在最後執行其代碼塊。

如何處理循環

如咱們普通的循環寫法。用for來循環迭代值

int getTotal(List<int> numbers){
   int total = 0;
   for(final value in numbers){
      total +=value;
   }
   return total;
}複製代碼

可是若是咱們這個函數讀取的是一個由數字組成的stream,該怎麼辦,按照他們的異步到達的次序來進行求和,而後返回求和值,和aync awaite的方式同樣,不用改變代碼基本結構。

首先,先將函數標誌位async

而後,將返回值更改成future

而後,在前面添加關鍵字await。完成。代碼以下:

Future<int> getTotal(List<int> numbers) async{
   int total = 0;
   await for(final value in numbers){
      total +=value;
   }
   return total;
}複製代碼

和future同樣,await關鍵字將個人函數,拆分爲等待事件以前執行和以後執行,兩部分代碼,首先,他開始執行,一直到await,而後將其future返回給調用函數,並等待一段數據到達。當數據到達時,循環執行一次來處理那條數據,而後停下來,並等待下一條數據,也許此時應用程序會運行並執行其餘一些操做,如垃圾回收,最終另外一條數據到來了,而後循環再次執行,這種狀況一直循環,直到stream結束並關閉,此時該函數退出循環,並執行其return語句。這樣就完成了getTotal在最初返回給它的調用者的feture。

有一點要注意的是,當你使用stream時,保證要處理的streams最終會完成。若是遇到如html查找一個按鈕點擊,這個按鈕一直沒出來就沒法完成,則函數會一直等待的狀態。

參考

Get started with Flutter → flutter.dev Try a Flutter codelab → goo.gl/d3fHPo Join the conversation → goo.gl/68oUnb

相關文章
相關標籤/搜索