文章授權(quán)首發(fā)于code4flutter,如需轉(zhuǎn)載請注明來源 https://code4flutter.com/async-await-usage/
本文是flutter油管教程第3篇,介紹async和await的使用和原理
引言
很多語言使用async和await作為語法,將函數(shù)變成async,然后在代碼的某個位置,它會進(jìn)行等待,直到任務(wù)處理完成返回。在flutter中也是這樣的基本功能,但是需要特別指出的是這種語法糖只是feature和stream的封裝,可以讓你更清晰的更易讀的代碼。
對比
這里是一個處理數(shù)據(jù)處理的例子,網(wǎng)絡(luò)請求和文件IO操作是異步操作
我們想讓他們放在一起工作,首先通過網(wǎng)絡(luò)請求創(chuàng)建一個id,根據(jù)這個id進(jìn)行網(wǎng)絡(luò)通信,然后用結(jié)果創(chuàng)建一個processdata的對象,使用dart的feature的api,然后將回調(diào)鏈接在一起。這樣用前一個的回調(diào)作為下一個回調(diào)的參數(shù)輸入,以此類推。但是之前的方式并不具有很好的可讀性,需要層層嵌套,但是用feature結(jié)合async和wait不一樣。
ProcessedData createData() async{ final id = _loadFromDisk(); final data = _fetchNetworkData(id); return ProcessedData(data); }復(fù)制代碼
首先,添加async關(guān)鍵字,只是告訴編譯器,“嘿,我打算在這里使用await關(guān)鍵字”
接下來我們來添加await關(guān)鍵字,在每個feature之前,標(biāo)志需要等待的函數(shù),
最后,在返回值添加feature關(guān)鍵字,有人覺得很奇怪,為什么只是普通的返回處理數(shù)據(jù)結(jié)果的函數(shù)需要feature修飾呢,這是因為在processData之前有兩次異步操作用到了await。這意味著他開始執(zhí)行,然后停止,等待磁盤事件,然后繼續(xù),然后停下來等待網(wǎng)絡(luò)事件。只有在那之后才能提供一個值。代碼如下:
ProcessedData createData() async{ final id = await _loadFromDisk(); final data = await _fetchNetworkData(id); return ProcessedData(data); }復(fù)制代碼
asyc/await 工作原理
所以當(dāng)createData開始運行并觸發(fā)第一個await時,他才會返回一個feature,到調(diào)用函數(shù),他說,“嘿,我需要等待一下東西”,這是一個空盒子,你需要等待一會兒,當(dāng)我在網(wǎng)絡(luò)中等待的磁盤數(shù)據(jù)到達(dá)時,我將調(diào)用return語句并為你提供一些數(shù)據(jù),把它放入futurebuilder,或其他東西中,這就是他的工作原理。用代碼翻譯如下:
Future<ProcessedData> createData(){ return _loadFromDisk().then((id){ return _fetchNetworkData(id); }).then((data){ return ProcessData(data); }) }復(fù)制代碼
用feature api的好處就是你可以清楚的看到代碼是如何將事件進(jìn)行拆分的。首先函數(shù)開始執(zhí)行,調(diào)用loadfromdisk,等待來來自磁盤的數(shù)據(jù)觸發(fā)事件循環(huán),然后第二個回調(diào)被調(diào)用,請求網(wǎng)絡(luò),等待來自網(wǎng)絡(luò)的數(shù)據(jù)到達(dá)事件循環(huán),然后處理數(shù)據(jù)。此時調(diào)用return,此時該值完成了。
事件循環(huán)
現(xiàn)在我們使用async/await版本實現(xiàn)。通過await表達(dá)式作為等待。由此可以得到以下代碼。
async/await正確姿勢
Future<ProcessedData>createData() async{ final id = await _loadFromDisk(); final data = await _fetchNetworkData(id); return ProcessedData(data); }復(fù)制代碼
createData開始執(zhí)行,并觸發(fā)第一個等待,此時它將自己的future返回給函數(shù),并調(diào)用loadFromDisk,然后像之前一樣,等待來自磁盤文件I/O事件,這樣就完成了由loadFromDisk返回的future,這意味著createData結(jié)束了等待,并可以繼續(xù)執(zhí)行其余的代碼了。再次等待,請求網(wǎng)絡(luò)數(shù)據(jù)等待返回,最終網(wǎng)絡(luò)數(shù)據(jù)到達(dá)事件循環(huán),這就完成了由fetchNetworkData返回的future。createData就可以再次繼續(xù)運行了,他創(chuàng)建并返回了一個ProcessedData的實例。他完成了createData在開頭處傳給調(diào)用者的那個future。
由此可以看到,相同的事件循環(huán)控制著代碼的運行,并涉及到相同的future,唯一變化的是,有了async/await,函數(shù)代碼變少了,看起來更像是同步的代碼。
async/await如何處理錯誤
普通的寫法,處理錯誤如下,
使用catcherror來處理錯誤,完成后悔在最后執(zhí)行回調(diào),無論是否有錯誤發(fā)生。
在aync/await的方式中不用任何回調(diào),而是用try catch
可以使用try catch捕獲特定類型的異常,finally將在最后執(zhí)行其代碼塊。
如何處理循環(huán)
如我們普通的循環(huán)寫法。用for來循環(huán)迭代值
int getTotal(List<int> numbers){ int total = 0; for(final value in numbers){ total +=value; } return total; }復(fù)制代碼
但是如果我們這個函數(shù)讀取的是一個由數(shù)字組成的stream,該怎么辦,按照他們的異步到達(dá)的次序來進(jìn)行求和,然后返回求和值,和aync awaite的方式一樣,不用改變代碼基本結(jié)構(gòu)。
首先,先將函數(shù)標(biāo)志位async
然后,將返回值更改為future
然后,在前面添加關(guān)鍵字await。完成。代碼如下:
Future<int> getTotal(List<int> numbers) async{ int total = 0; await for(final value in numbers){ total +=value; } return total; }復(fù)制代碼
和future一樣,await關(guān)鍵字將我的函數(shù),拆分為等待事件之前執(zhí)行和之后執(zhí)行,兩部分代碼,首先,他開始執(zhí)行,一直到await,然后將其future返回給調(diào)用函數(shù),并等待一段數(shù)據(jù)到達(dá)。當(dāng)數(shù)據(jù)到達(dá)時,循環(huán)執(zhí)行一次來處理那條數(shù)據(jù),然后停下來,并等待下一條數(shù)據(jù),也許此時應(yīng)用程序會運行并執(zhí)行其他一些操作,如垃圾回收,最終另一條數(shù)據(jù)到來了,然后循環(huán)再次執(zhí)行,這種情況一直循環(huán),直到stream結(jié)束并關(guān)閉,此時該函數(shù)退出循環(huán),并執(zhí)行其return語句。這樣就完成了getTotal在最初返回給它的調(diào)用者的feture。
有一點要注意的是,當(dāng)你使用stream時,保證要處理的streams最終會完成。如果遇到如html查找一個按鈕點擊,這個按鈕一直沒出來就無法完成,則函數(shù)會一直等待的狀態(tài)。
參考
Get started with Flutter → flutter.dev Try a Flutter codelab → goo.gl/d3fHPo Join the conversation → goo.gl/68oUnb