应用办公生活信息教育商业
投稿投诉
商业财经
汽车智能
教育国际
房产环球
信息数码
热点科技
生活手机
晨报新闻
办公软件
科学动态
应用生物
体育时事

Flutter异步编程指南

  作者:京东物流王志明1Dart中的事件循环模型
  在App开发中,经常会遇到处理异步任务的场景,如网络请求、读写文件等。Android、iOS使用的是多线程,而在Flutter中为单线程事件循环,如下图所示
  Dart中有两个任务队列,分别为microtask队列和event队列,队列中的任务按照先进先出的顺序执行,而microtask队列的执行优先级高于event队列。在main方法执行完毕后,会启动事件循环,首先将microtask队列中的任务逐个执行完毕,再去执行event队列中的任务,每一个event队列中的任务在执行完成后,会再去优先执行microtask队列中的任务,如此反复,直到清空所有队列,这个过程就是Dart事件循环的处理机制。这种机制可以让我们更简单的处理异步任务,不用担心锁的问题。我们可以很容易的预测任务执行的顺序,但无法准确的预测到事件循环何时会处理到你期望执行的任务。例如创建了一个延时任务,但排在前面的任务结束前是不会处理这个延时任务的,也就说这个任务的等待时间可能会大于指定的延迟时间。
  Dart中的方法一旦开始执行就不会被打断,而event队列中的事件还来自于用户输入、IO、定时器、绘制等,这意味着在两个队列中都不适合执行计算量过大的任务,才能保证流畅的UI绘制和用户事件的快速响应。而且当一个任务的代码发生异常时,只会打断当前任务,后续任务不受影响,程序更不会退出。从上图还可以看出,将一个任务加入microtask队列,可以提高任务优先级,但是一般不建议这么做,除非比较紧急的任务并且计算量不大,因为UI绘制和处理用户事件是在event事件队列中的,滥用microtask队列可能会影响用户体验。
  总结下Dart事件循环的主要概念:Dart中有两个队列来执行任务:microtask队列和event队列。事件循环在main方法执行完毕后启动,microtask队列中的任务会被优先处理。microtask队列只处理来自Dart内部的任务,event队列中有来自Dart内部的Future、Timer、isolatemessage,还有来自系统的用户输入、IO、UI绘制等外部事件任务。Dart中的方法执行不会被打断,因此两个队列中都不适合用来执行计算量大的任务。一个任务中未被处理的异常只会打断当前任务,后续任务不受影响,程序更不会退出。1。1向microtask队列中添加任务
  可以使用顶层方法scheduleMicrotask或者Future。microtask方法,如下所示:scheduleMicrotask(()print(microtask1));Future。microtask(()print(microtask2));
  使用Future。microtask的优势在于可以在then回调中处理任务返回的结果。1。2向event队列中添加任务Future(()print(eventtask));
  基于以上理论,通过如下代码可以验证Dart的事件循环机制:voidmain(){print(mainstart);Future(()print(eventtask1));Future。microtask(()print(microtask1));Future(()print(eventtask1));Future。microtask(()print(microtask2));print(mainstop);
  执行结果:mainstartmainstopmicrotask1microtask2eventtask1eventtask1
  通过输出结果可以看到,任务的执行顺序并不是按照编写代码的顺序来的,将任务添加到队列不会立刻执行,而执行顺序也完全符合前面讲的规则,当前main方法中的代码执行完毕后,才会去执行队列中的任务,且microTask队列的优先级高于event队列。2Dart中的异步实现
  在Dart中通过Future来执行异步任务,Future是对异步任务状态的封装,对任务结果的代理,通过then方法可以注册处理任务结果的回调方法。
  创建方法Future方式:
  Future()
  Future。delayed()
  Future。microtask()
  Future。sync()2。1Future()factoryFuture(FutureOrTcomputation()){FutureTresultnewFutureT();Timer。run((){try{result。complete(computation());}catch(e,s){completeWithErrorCallback(result,e,s);}});returnresult;}
  上面是Future()的源码,可以看到内部是通过启动一个没有延迟的计时器来添加任务的,实用trycatch来捕获任务代码中可能出现的异常,我们可以在catchError回调中来处理异常。2。2Future。delayed()factoryFuture。delayed(Durationduration,〔FutureOrTcomputation()?〕){if(computationnull!typeAcceptsNullT()){throwArgumentError。value(null,computation,Thetypeparameterisnotnullable);}FutureTresultnewFutureT();newTimer(duration,(){if(computationnull){result。complete(nullasT);}else{try{result。complete(computation());}catch(e,s){completeWithErrorCallback(result,e,s);}}});returnresult;}
  Future。delayed()与Future()的区别是通过一个延迟的计时器来添加任务。2。3Future。microtask()factoryFuture。microtask(FutureOrTcomputation()){FutureTresultnewFutureT();scheduleMicrotask((){try{result。complete(computation());}catch(e,s){completeWithErrorCallback(result,e,s);}});returnresult;}
  Future。microtask()是将任务添加到microtask队列,通过这种可以很方便通过then方法中的回调来处理任务的结果。2。4Future。sync()factoryFuture。sync(FutureOrTcomputation()){try{varresultcomputation();if(resultisFutureT){returnresult;}else{TODO(40014):Removecastwhentypepromotionworks。returnnewFutureT。value(resultasdynamic);}}catch(error,stackTrace){varfuturenewFutureT();AsyncError?replacementZone。current。errorCallback(error,stackTrace);if(replacement!null){future。asyncCompleteError(replacement。error,replacement。stackTrace);}else{future。asyncCompleteError(error,stackTrace);}returnfuture;}}
  Future。sync()中的任务会被立即执行,不会添加到任何队列。
  在第一个章节中讲到了可以很容易的预测任务的执行顺序,下面我们通过一个例子来验证:voidmain(){print(mainstart);Future。microtask(()print(microtask1));Future。delayed(newDuration(seconds:1),()print(delayedevent));Future(()print(event1));Future(()print(event2));Future。microtask(()print(microtask2));print(mainstop);}
  执行结果:mainstartmainstopmicrotask1microtask2event1event2delayedevent
  因为代码比较简单,通过代码可以很容易的预测到执行结果,下面将复杂度稍微提高。voidmain(){print(mainstart);Future。microtask(()print(microtask1));Future。delayed(newDuration(seconds:1),()print(delayedevent));Future(()print(event1))。then(()print(event1callback1))。then(()print(event1callback2));Future(()print(event2))。then((){print(event2callback1);returnFuture(()print(event4))。then(()print(event4callback));})。then((){print(event2callback2);Future(()print(event5))。then(()print(event5callback));})。then((){print(event2callback3);Future。microtask(()print(microtask3));})。then((){print(event2callback4);});Future(()print(event3));Future。sync(()print(synctask));Future。microtask(()print(microtask2))。then(()print(microtask2callbak));print(mainstop);}
  执行结果:mainstartsynctaskmainstopmicrotask1microtask2microtask2callbakevent1event1callback1event1callback2event2event2callback1event3event4event4callbackevent2callback2event2callback3event2callback4microtask3event5event5callbackdelayedevent
  看到结果后你可能会疑惑,为什么event1、event1callback1、event1callback2会连续输出,而event2callback1输出后为什么是event3,event5、event5callback为什么会在microtask3后输出?
  这里我们补充下then方法的一些关键知识,理解了这些,上面的输出结果也就很好理解了:then方法中的回调并不是按照它们注册的顺序来执行。Future中的任务执行完毕后会立刻执行then方法中的回调,并且回调不会被添加到任何队列中。如果Future中的任务在then方法调用之前已经执行完毕了,那么会有一个任务被加入到microtask队列中。这个任务执行的就是被传入then方法中的回调。2。5catchError、whenCompleteFuture((){throwerror;})。then((){print(success);})。catchError((error){print(error);})。whenComplete((){print(completed);});
  输出结果:errorcompleted
  通过catchError方法注册的回调,可以用来处理任务代码产生的异常。不管Future中的任务执行成功与否,whenComplete方法都会被调用。2。6async、await
  使用async、await能以更简洁的编写异步代码,是Dart提供的一个语法糖。使用async关键字修饰的方法返回值类型为Future,在async方法内可以使用await关键字来修饰异步任务,在方法内部达到同步执行的效果,可以达到简化代码和提高可读性的效果,不过如果想要处理异常,需要实用trycatch语句来包裹await修饰的异步任务。voidmain()async{print(awaitgetData());}FutureintgetData()async{finalaawaitFuture。delayed(Duration(seconds:1),()1);finalbawaitFuture。delayed(Duration(seconds:1),()1);returnab;}3Isolate介绍
  前面讲到耗时任务不适合放到microtask队列或event队列中执行,会导致UI卡顿。那么在Flutter中有没有既可以执行耗时任务又不影响UI绘制呢,其实是有的,前面提到microtask队列和event队列是在mainisolate中运行的,而isolate是在线程中运行的,那我们开启一个新的isolate就可以了,相当于开启一个新的线程,使用多线程的方式来执行任务,Flutter也为我们提供了相应的Api。3。1computevoidmain()async{computeString,String(getData,Alex,)。then((result){print(result);});}StringgetData(Stringname){模拟耗时3秒sleep(Duration(seconds:3));returnHelloname;}
  compute第一个参数是要执行的任务,第二个参数是要向任务发送的消息,需要注意的是第一个参数只支持顶层参数。使用compute()可以方便的执行耗时任务,但是滥用的话也会适得其反,因为每次调用,相当于新建一个isolate。上面的代码执行一个经历了isolate的创建以及销毁过程,还有数据的传递会经历两次拷贝,因为isolate之间是完全隔离的,不能共享内存,整个过程除去任务本身的执行时间,也会非常的耗时,isolate的创建也比较消耗内存,创建过多的isolate还有OOM的风险。这时我们就需要一个更优的解决方案,减少频繁创建销毁isolate所带来的消耗,最好是能创建一个类似于线程池的东西,只要提前初始化好,后面就可以随时使用,不用担心会发生前面所讲的问题,这时候LoadBalancer就派上用场了3。2LoadBalancer用来创建LoadBalancerFutureLoadBalancerloadBalancerCreatorLoadBalancer。create(2,IsolateRunner。spawn);全局可用的loadBalancerlateLoadBalancerloadBalancer;voidmain()async{初始化LoadBalancerloadBalancerawaitloadBalancerCreator;使用LoadBalancer执行任务finalresultawaitloadBalancer。runString,String(getData,Alex);print(result);}StringgetData(Stringname){模拟耗时3秒sleep(Duration(seconds:3));returnHelloname;}
  使用LoadBalancer。create()方法可以创建出一个isolate线程池,能够指定isolate的数量,并自动实现了负载均衡。应用启动后在合适的时机将其初始化好,后续就有一个全局可用的LoadBalancer了。4实用经验4。1指定任务的执行顺序
  在开发中经常会有需要连续执行异步任务的场景,例如下面的例子,后面的一步任务直接需要以来前面任务的结果,所有任务正常执行完毕才算成功。voidmain()async{print(awaitgetData());}FutureintgetData(){finalcompleterCompleterint();intvalue0;Future((){return1;})。then((result1){valueresult1;returnFuture((){return2;})。then((result2){valueresult2;returnFuture((){return3;})。then((result3){valueresult3;completer。complete(value);});});});returncompleter。future;}
  这种方式出现了回调地狱,代码非常难以阅读,实际开发中还会有处理异常的代码,会显得更加臃肿,编写难度也大,显然这种方式是不建议使用的。4。2使用then的链式调用voidmain()async{print(awaitgetData());}FutureintgetData(){intvalue0;returnFuture(()1)。then((result1){valueresult1;returnFuture(()2);})。then((result2){valueresult2;returnFuture(()3);})。then((result3){valueresult3;returnvalue;});}
  回调地狱的问题解决了,代码可读性提高很多。4。3使用async、awaitvoidmain()async{print(awaitgetData());}FutureintgetData()async{intvalue0;valueawaitFuture(()1);valueawaitFuture(()2);valueawaitFuture(()3);returnvalue;}
  效果显而易见,代码更加清晰了。4。4取消任务
  在前面讲到了Dart方法执行时是不能被中断的,这就意味着一个Future任务开始后必然会走到完成的状态,但是很多时候我们需要又取消一个异步任务,唯一的办法就是在任务结束后不执行回调代码,就可以实现类似取消的效果。4。5CancelableOperation
  在Flutter的async包中,提供了一个CancelableOperation给我们使用,使用它可以很简单的实现取消任务的需求。voidmain()async{创建一个可以取消的任务finalcancelableOperationCancelableOperation。fromFuture(Future(()async{print(start);awaitFuture。delayed(Duration(seconds:3));模拟耗时3秒print(end);}),onCancel:()print(cancel。。。),);注册任务结束后的回调cancelableOperation。value。then((val){print(finished);});模拟1秒后取消任务Future。delayed(Duration(seconds:1))。then(()cancelableOperation。cancel());}
  CancelableOperation是对Future的代理,对Future的then进行了接管,判断isCanceled标记决定是否需要执行用户提供的回调。

李嘉诚父子,一把卖出119栋豪宅来源:视觉中国记者钟黛杨松编辑陈晓平李嘉诚家族的买卖,一刻不停歇。在9月最后一周,李家地产旗舰长实集团(01113。HK),股价遭遇重挫,创下半年新低,多轮回……在北京,压力都是自己给的前几天,公司组织部门聚餐,地点选在了双井附近的一家日料店,聚餐结束后已是晚上九点多,马路两边的各种露天饭馆灯红酒绿,人声湮没在觥筹交错之中,空气里弥漫着烧烤的香味,远处的富力广……这样的家庭培养出来的孩子容易得抑郁症穷凶极恶的人,没有罪恶感,所以什么坏事都能干得出来,同样伪善的人为了逃避罪恶感,什么邪恶的事都会去干。一定要远离伪善之人,否则它被扼杀你生命中的活力。伪善之人和正常……几百元的波轮洗衣机,和两三千元的有什么区别?有必要买贵的吗?很多人家里都用上滚筒洗衣机了,还在用波轮洗衣机的家庭并不多,如今还坚持用波轮洗衣机的人,很大的原因是由于波轮洗衣机价格便宜,性价比高,而且操作简单方便,洗衣服也干净,完美诠释了……乌镇入风口来乌镇,看未来。始于2014年的世界互联网大会,已经走到第九个年头。互联网风起云涌,就如桥下千年流淌的京杭大运河,让千年古镇联通万物。深度覆盖的5G网络、招手即停的无人驾……英特尔确认CPU及各类芯片涨价,将于2022Q4开始执行新定此前有报道称,英特尔基于能源、原材料和劳动力成本增加等因素,加上已存在供过于求的问题,计划提高其产品的定价,大部分CPU及各类芯片都会受此影响。传闻英特尔会在现有定价基础上增加……若是场戏同是天涯沦落人,相逢何必曾相识。总在错误年纪错误地方,遇到情投意合的人。而自己的内心却一次次囚禁在过往愚昧而执着的誓言里。虽然曾经那人早已离去,却始终打不开困扰自己多年的枷锁。……学术前沿袁维堂教授团队在机器人中低位直肠癌保肛手术方面取得新近日,郑州大学第一附属医院直肠肛门外科袁维堂教授团队参与的全国多中心研究机器人对比腹腔镜中低位直肠癌保肛手术(REAL研究)取得新突破,研究成果以题为Roboticversus……经受多重考验我国如何实现粮食稳产丰收?粮稳天下安。我国的粮食安全,关系着十四亿多人口的吃饭问题。今年,经受住多重考验,我国粮食的稳产丰收来之不易。守住耕地红线去年全国耕地总量净增加今年,我国夏粮、早稻、……人到了一定年纪,拼的是格局你的能力决定你能得到什么,而你的格局,却会决定你最终能走到哪里。到了一定年纪,要想走得远,一定要修得大格局,能有容人、容言、容事之量。容人古人云:让人三分不吃……暗黑破坏神不朽德鲁伊的藤蔓,那股画风让我想起了虫族暗黑2的暗金神器到了3都成了硫磺,好气啊!现在2重置版又没希望了,4到底怎么样还是未知数,好在有了官方渠道买2,召唤以前的室友们又在战网走起了。一个boss战打成穷光蛋,老老实……俄罗斯三套娃缺席!世锦赛成就坂本花织,236。09分夺冠,挥花样滑冰世锦赛今天凌晨继续进行,在缺少了俄罗斯三套娃之后,这次的女单比赛也是缺少了不少的星光,但是作为新科的冬奥会季军,日本队的坂本花织也是成为夺冠的最大热门,而她的表现也是无……
售价5。29万元体验小虎FEV长续航版城市代步好选择很多人可能会对小虎汽车这个名字有点陌生,实际上小虎汽车是由东风汽车有限公司、郑州日产有限公司和北京宏瑞汽车有限公司三方合作推出的一款A00级新能源汽车,主打家用,并提出了FEV……娇艳玫瑰清风共舞玫瑰娇艳,随风摇曳着身姿,或粉嫩或鲜红的花瓣在绿叶衬托下婀娜多姿,绽放在衣裙间更是浪漫清新,惹人沉醉。孙允珠韩国模特孙允珠的碎花连衣裙之前有盘点过:小仙女的碎花裙、……湖北朝兴网络科技有限公司中秋佳节送长辈健康智能手表,守护爱大家好,小编我又来了,传统中秋佳节还有一个月左右时间就要到了,这段日子大家肯定都已经提前在安排,也有在给长辈提前准备礼物的,中秋送礼物大家是不是都是下面这样的中秋礼物,主……茅台8月14日行情,最新消息散装原箱均跌,生肖系列均涨。仁怀突发疫情,近期快递,货运会受影响!!!行情价:茅台酒商之间互相调货价回收价:茅台散瓶价浮动050左右,原箱价浮动50150左右……鸿蒙设备数突破4。7亿台,3年发展自主系统华为度过混沌初开阶据上证报报道称,华为鸿蒙设备数鸿蒙智联设备数突破4。7亿台、HarmonyOS3。0正式发布、开源鸿蒙(OpenHarmony)已推出14个行业发行版近期,有关鸿蒙的新进展不断……随手买房的李湘,她让人羡慕的可不仅仅是壕李想的冰箱一打开,所有在场所有人都惊呆了。冰箱里所有昂贵的护肤品都是海参、虫草、燕窝等。据统计,李想冰箱里的食材总价值超过60万元,是该计划有史以来最贵的冰箱。李想坦言,……10月面板价格已上涨,头部企业TCL科技或将充分受益近日,包括WitsView、群智咨询、DISCIEN等在内的多家第三方调研机构相继公布10月份TV面板价格迎来上涨。最近几个月,全球主要面板厂下定决心降低稼动率以控制产出,随着……中国新闻出版研究院数字研究所所长王飙移动游戏数字营销趋势12月14日至16日,2021年度中国游戏产业年会于广州汇华希尔顿逸林酒店与广州科学城会议中心举办。在游戏出海与传播论坛上,中国新闻出版研究院数字研究所所长王飙发布了《2020……盘点那些年CPU中的神U,你有用过吗?头条创作挑战赛CPU的种类、数量之多不可量化,但总有一些特别的CPU会让玩家记忆深刻,那些CPU就是通常所说的神U。之所以被称为神U,主要原因在于游戏和视频处理时所需要的……美国名将跑出9秒76!超越苏炳添亚洲纪录创历史,成田径史上第美国运动员克利成绩9秒76,在2022年美国田径锦标赛上打破了苏炳添和克利超越苏炳添的纪录。克利他不仅取得了个人最好成绩,也是今年的最佳战绩。他也是百米历史上的第六人。克利今年……国乒终于回家!教练直言顶不住了!大师姐与小师妹太暖心随着新加坡世界杯决赛的落幕,国乒再次包揽男女单打的冠军。而在稍稍休息之后,他们便搭乘飞机踏上归国之路。目前,全队上下已经顺利抵达山东威海,进行为期两周的封闭隔离。由于连续参加世……诱人的草莓,真的有激素吗虽然是寒冷的冬季,北风刺骨,但水灵灵的草莓已经开始上市了,红彤彤、个头也很大,看着十分诱人。当然这时候上市的草莓都是在大棚里种植的,小编割肉买了点,口感还不错。这时候很多家长可……
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网