摘要:包含所有外來(lái)的事件,,,,之間的等。當(dāng)定義函數(shù)時(shí),還可以指定其運(yùn)行結(jié)果返回值的類(lèi)型,以提高代碼的可讀性定義了返回結(jié)果值為類(lèi)型因?yàn)轭?lèi)型不匹配,會(huì)報(bào)錯(cuò)最主要的功能就是提供了鏈?zhǔn)秸{(diào)用。
由于前面的HTTP請(qǐng)求用到了異步操作,不少小伙伴都被這個(gè)問(wèn)題折了下腰,今天總結(jié)分享下實(shí)戰(zhàn)成果。Dart是一個(gè)單線程的語(yǔ)言,遇到有延遲的運(yùn)算(比如IO操作、延時(shí)執(zhí)行)時(shí),線程中按順序執(zhí)行的運(yùn)算就會(huì)阻塞,用戶就會(huì)感覺(jué)到卡頓,于是通常用異步處理來(lái)解決這個(gè)問(wèn)題。當(dāng)遇到有需要延遲的運(yùn)算(async)時(shí),將其放入到延遲運(yùn)算的隊(duì)列(await)中去,把不需要延遲運(yùn)算的部分先執(zhí)行掉,最后再來(lái)處理延遲運(yùn)算的部分。
async和await首先看一個(gè)案例:
//HTTP的get請(qǐng)求返回值為Future類(lèi)型,即其返回值未來(lái)是一個(gè)String類(lèi)型的值 getData() async { //async關(guān)鍵字聲明該函數(shù)內(nèi)部有代碼需要延遲執(zhí)行 return await http.get(Uri.encodeFull(url), headers: {"Accept": "application/json"}); //await關(guān)鍵字聲明運(yùn)算為延遲執(zhí)行,然后return運(yùn)算結(jié)果 }
然后我們調(diào)用這個(gè)函數(shù),想獲取其結(jié)果:
String data = getData();
在書(shū)寫(xiě)時(shí),在IDE中這個(gè)代碼是沒(méi)有問(wèn)題的,但是當(dāng)我們運(yùn)行這段代碼時(shí),就報(bào)錯(cuò)了:
為什么呢?因?yàn)?b>data是String類(lèi)型,而函數(shù)getData()是一個(gè)異步操作函數(shù),其返回值是一個(gè)await延遲執(zhí)行的結(jié)果。在Dart中,有await標(biāo)記的運(yùn)算,其結(jié)果值都是一個(gè)Future對(duì)象,Future不是String類(lèi)型,所以就報(bào)錯(cuò)了。
那如果這樣的話,我們就沒(méi)法獲取到延遲執(zhí)行的結(jié)果了?當(dāng)然可以,Dart規(guī)定有async標(biāo)記的函數(shù),只能由await來(lái)調(diào)用,比如這樣:
String data = await getData();
但是要使用await,必須在有async標(biāo)記的函數(shù)中運(yùn)行,否則這個(gè)await會(huì)報(bào)錯(cuò):
于是,我們要為這個(gè)給data賦值的語(yǔ)句加一個(gè)async函數(shù)的包裝:
String data; setData() async { data = await getData(); //getData()延遲執(zhí)行后賦值給data }
上面這種方法一般用于調(diào)用封裝好的異步接口,比如getData()被封裝到了其他dart文件,通過(guò)使用async函數(shù)對(duì)其調(diào)取使用
再或者,我們?nèi)サ?b>async函數(shù)的包裝,在getData()中直接完成data變量的賦值:
String data; getData() async { data = await http.get(Uri.encodeFull(url), headers: {"Accept": "application/json"}); //延遲執(zhí)行后賦值給data }
這樣,data就獲取到HTTP請(qǐng)求的數(shù)據(jù)了。就這樣就完了?是滴,只要記住兩點(diǎn):
await關(guān)鍵字必須在async函數(shù)內(nèi)部使用
調(diào)用async函數(shù)必須使用await關(guān)鍵字
PS:await關(guān)鍵字真的很形象,等一等的意思,就是說(shuō),既然你運(yùn)行的時(shí)候都要等一等,那我調(diào)用的時(shí)候也等一等吧Future簡(jiǎn)單科普
前面?zhèn)€講到過(guò),直接return await ...的時(shí)候,實(shí)際上返回的是一個(gè)延遲計(jì)算的Future對(duì)象,這個(gè)Future對(duì)象是Dart內(nèi)置的,有自己的隊(duì)列策略,我們就來(lái)聊聊這個(gè)Future。
先啰嗦一些關(guān)于Dart在線程方面的知識(shí)。
Dart是基于單線程模型的語(yǔ)言。在Dart也有自己的進(jìn)程(或者叫線程)機(jī)制,名叫isolate。APP的啟動(dòng)入口main函數(shù)就是一個(gè)isolate。玩家也可以通過(guò)引入import "dart:isolate"創(chuàng)建自己的isolate,對(duì)多核CPU的特性來(lái)說(shuō),多個(gè)isolate可以顯著提高運(yùn)算效率,當(dāng)然也要適當(dāng)控制isolate的數(shù)量,不應(yīng)濫用,否則走火入魔自廢武功。有一個(gè)很重要的點(diǎn),Dart中isolate之間無(wú)法直接共享內(nèi)存,不同的isolate之間只能通過(guò)isolate API進(jìn)行通信,當(dāng)然本篇的重點(diǎn)在于Future,不展開(kāi)講isolate,心急的小伙伴可以參考官方閱讀理解或者參考大神tain335的人肉翻譯。
Dart線程中有一個(gè)消息循環(huán)機(jī)制(event loop)和兩個(gè)隊(duì)列(event queue和microtask queue)。
event queue包含所有外來(lái)的事件:I/O,mouse events,drawing events,timers,isolate之間的message等。任意isolate中新增的event(I/O,mouse events,drawing events,timers,isolate的message)都會(huì)放入event queue中排隊(duì)等待執(zhí)行,好比機(jī)場(chǎng)的公共排隊(duì)大廳。
microtask queue只在當(dāng)前isolate的任務(wù)隊(duì)列中排隊(duì),優(yōu)先級(jí)高于event queue,好比機(jī)場(chǎng)里的某個(gè)VIP候機(jī)室,總是VIP用戶先登機(jī)了,才開(kāi)放公共排隊(duì)入口。
如果在event中插入microtask,當(dāng)前event執(zhí)行完畢即可插隊(duì)執(zhí)行microtask。如果沒(méi)有microtask,就沒(méi)辦法插隊(duì)了,也就是說(shuō),microtask queue的存在為Dart提供了給任務(wù)隊(duì)列插隊(duì)的解決方案。
當(dāng)main方法執(zhí)行完畢退出后,event loop就會(huì)以FIFO(先進(jìn)先出)的順序執(zhí)行microtask,當(dāng)所有microtask執(zhí)行完后它會(huì)從event queue中取事件并執(zhí)行。如此反復(fù),直到兩個(gè)隊(duì)列都為空,如下流程圖:
注意:當(dāng)事件循環(huán)正在處理microtask的時(shí)候,event queue會(huì)被堵塞。這時(shí)候app就無(wú)法進(jìn)行UI繪制,響應(yīng)鼠標(biāo)事件和I/O等事件。胡亂插隊(duì)也是有代價(jià)的~
雖然你可以預(yù)測(cè)任務(wù)執(zhí)行的順序,但你無(wú)法準(zhǔn)確的預(yù)測(cè)到事件循環(huán)何時(shí)會(huì)處理你期望的任務(wù)。例如當(dāng)你創(chuàng)建一個(gè)延時(shí)1s的任務(wù),但在排在你之前的任務(wù)結(jié)束前事件循環(huán)是不會(huì)處理這個(gè)延時(shí)任務(wù)的,也就是或任務(wù)執(zhí)行可能是大于1s的。
OK,了解以上信息之后,再來(lái)回到Future,小伙伴可能已經(jīng)被繞暈了。
Future就是event,很多Flutter內(nèi)置的組件比如前幾篇用到的Http(http請(qǐng)求控件)的get函數(shù)、RefreshIndicator(下拉手勢(shì)刷新控件)的onRefresh函數(shù)都是event。每一個(gè)被await標(biāo)記的句柄也是一個(gè)event,每創(chuàng)建一個(gè)Future就會(huì)把這個(gè)Future扔進(jìn)event queue中排隊(duì)等候安檢~
什么?那microtask呢?當(dāng)然不會(huì)忘了這個(gè),scheduleMicrotask,用法和Future基本一樣。
為什么要用Future?前面講到,用async和await組合,即可向event queue中插入event實(shí)現(xiàn)異步操作,好像Future的存在有些多余的感覺(jué),剛開(kāi)始我本人也有這樣的疑惑,且往下看。
當(dāng)定義Flutter函數(shù)時(shí),還可以指定其運(yùn)行結(jié)果返回值的類(lèi)型,以提高代碼的可讀性:
//定義了返回結(jié)果值為String類(lèi)型 FuturegetDatas(String category) async { var request = await _httpClient.getUrl(Uri.parse(url)); var response = await request.close(); return await response.transform(utf8.decoder).join(); } run() async{ int data = await getDatas("keji"); //因?yàn)轭?lèi)型不匹配,IDE會(huì)報(bào)錯(cuò) }
Future最主要的功能就是提供了鏈?zhǔn)秸{(diào)用。熟悉ES6語(yǔ)法的小伙伴樂(lè)開(kāi)了花,鏈?zhǔn)秸{(diào)用解決兩大問(wèn)題:明確代碼執(zhí)行的依賴(lài)關(guān)系和實(shí)現(xiàn)異常捕獲。WTF?還不明白?且看下面這些案例:
//案例1 funA() async{ ...set an important variable... } funB() async{ await funA(); ...use the important variable... } main() async { funB(); } //如果要想先執(zhí)行funA再執(zhí)行funB,必須在funB中await funA(); //funB的代碼與funA耦合,將來(lái)如果funA廢掉或者改動(dòng),funB中還需要經(jīng)過(guò)修改以適配變更。 //案例2 funA() async{ try{ ...set an important variable... }catch(e){ do sth... }finally{ do sth. else... } } funB() async{ try{ ...use the important variable... }catch(e){ do sth... }finally{ do sth. else... } } main() async { await funA(); await funB(); } //沒(méi)有明確體現(xiàn)出設(shè)置變量和使用變量之間的依賴(lài)關(guān)系,其他開(kāi)發(fā)者難以理解你的代碼邏輯,代碼維護(hù)困難 //并且如果為了防止funA()或者funB()因發(fā)生異常導(dǎo)致程序崩潰 //要到funA()或者funB()中分別加入`try`、`catch`、`finally`
為了解決上面的問(wèn)題,Future提供了一套非常簡(jiǎn)潔的解決方案:
//案例3 funA(){ ...set an important variable... //設(shè)置變量 } funB(){ ...use the important variable... //使用變量 } main(){ new Future.then(funA()).then(funB()); // 明確表現(xiàn)出了后者依賴(lài)前者設(shè)置的變量值 new Future.then(funA()).then((_) {new Future(funB())}); //還可以這樣用 //鏈?zhǔn)秸{(diào)用,捕獲異常 new Future.then(funA(),onError: (e) { handleError(e); }).then(funB(),onError: (e) { handleError(e); }) }
案例3的玩法是async和await無(wú)法企及的,因此掌握Future還是很有必要滴。當(dāng)然了,Future的玩法不僅僅局限于案例3,還有很多有趣的玩法,包括和microtask對(duì)象scheduleMicrotask配合使用,我這里就不一一介紹了,大家參考大神tain335的人肉翻譯或者官網(wǎng)閱讀理解吧。
總結(jié)Dart的isolate中加入了event queue和microtask queue后,有了一點(diǎn)協(xié)程的感覺(jué),或許這就是Flutter為啥在性能上敢和原生開(kāi)發(fā)叫板的原因之一吧。本篇的內(nèi)容比較抽象,如果還是有不明白的小伙伴,歡迎留言提問(wèn),我盡量回答,哈哈哈,就醬,歡迎加入到Flutter圈子或flutter 中文社區(qū)(官方QQ群:338252156),群里有前后端及全棧各路大神鎮(zhèn)場(chǎng)子,加入進(jìn)來(lái)沒(méi)事就寫(xiě)寫(xiě)APP掙點(diǎn)外快(這個(gè)真的有),順便翻譯翻譯官方英文原稿拉一票粉絲,一舉多得何樂(lè)而不為呢。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/94250.html
摘要:中的的線程是以事件循環(huán)和消息隊(duì)列的形式存在,包含兩個(gè)任務(wù)隊(duì)列,一個(gè)是內(nèi)部隊(duì)列,一個(gè)是外部隊(duì)列,而的優(yōu)先級(jí)又高于。同時(shí)還有處理按住時(shí)的事件額外處理,同時(shí)手勢(shì)處理一般在的子類(lèi)進(jìn)行。谷歌大會(huì)之后,有不少人咨詢了我 Flutter 相關(guān)的問(wèn)題,其中有不少是和面試相關(guān)的,如今一些招聘上也開(kāi)始羅列 Flutter 相關(guān)要求,最后想了想還是寫(xiě)一期總結(jié)吧,也算是 Flutter 的階段復(fù)習(xí)。 ??系統(tǒng)完...
摘要:上一篇我們實(shí)現(xiàn)了新聞列表,但在網(wǎng)絡(luò)不好的時(shí)候列表會(huì)白屏,因此為了提高使用體驗(yàn),往列表中加入懶加載效果。其次,引入一個(gè)內(nèi)置的手勢(shì)控件,用于支持下拉刷新列表的效果如下圖將異步執(zhí)行也進(jìn)行了控件化處理,即。 上一篇我們實(shí)現(xiàn)了新聞列表,但在網(wǎng)絡(luò)不好的時(shí)候列表會(huì)白屏,因此為了提高使用體驗(yàn),往列表中加入懶加載效果。其次,引入一個(gè)Flutter內(nèi)置的手勢(shì)控件,用于支持下拉刷新列表的效果如下圖: sho...
閱讀 2673·2021-11-11 16:54
閱讀 3671·2021-08-16 10:46
閱讀 3451·2019-08-30 14:18
閱讀 3045·2019-08-30 14:01
閱讀 2731·2019-08-29 14:15
閱讀 2016·2019-08-29 11:31
閱讀 3093·2019-08-29 11:05
閱讀 2597·2019-08-26 11:54