Flutter_Dart中的异步,Android开发需要学什么

本文详细介绍了Dart中的异步编程概念,包括Timer.run用于事件异步执行,Future的多种构造方法及then、catchError、whenComplete的使用,以及如何避免回调地狱。此外,还讲解了async/await关键字如何简化异步代码,通过示例展示了其同步代码形式的异步执行特性。
摘要由CSDN通过智能技术生成
  • 可以调用Timer.run来让代码以Event的方式异步执行
 Timer.run((){
       print('a event');
   }); 

好了,现在你知道怎么让你的Dart代码异步执行了。看起来并不是很复杂,但是你需要清楚的知道你的异步代码执行的顺序。这也是很多前端面试时候会问到的问题。举个简单的例子,请问下面这段代码是否会输出"executed"?

main() {
     Timer.run(() { print("executed"); });  
      foo() {
        scheduleMicrotask(foo);  
      }
      foo();
    } 

答案是不会,因为在始终会有一个foo存在于微任务队列。导致Event Loop没有机会去处理事件队列。还有更复杂的一些例子会有大量的异步代码混合嵌套起来然后问你执行顺序是什么样的,这都需要按照上述Event Loop规则仔细去分析。

和JS一样,仅仅使用回调函数来做异步的话很容易陷入“回调地狱(Callback hell)”,为了避免这样的问题,JS引入了Promise。同样的, Dart引入了Future

Future

要使用Future的话需要引入dart.async

import 'dart:async'; 

Future提供了一系列构造函数供你选择。

创建一个立刻在事件队列里运行的Future:

Future(() => print('立刻在Event queue中运行的Future')); 

创建一个延时1秒在事件队列里运行的Future:

Future.delayed(const Duration(seconds:1), () => print('1秒后在Event queue中运行的Future')); 

创建一个在微任务队列里运行的Future:

Future.microtask(() => print('在Microtask queue里运行的Future')); 

创建一个同步运行的Future:

Future.sync(() => print('同步运行的Future')); 

对,你没看错,同步运行的。

这里要注意一下,这个同步运行指的是构造Future的时候传入的函数是同步运行的,这个Future通过then串进来的回调函数是调度到微任务队列异步执行的。

有了Future之后, 通过调用then来把回调函数串起来,这样就解决了"回调地狱"的问题。

Future(()=> print('task'))
    .then((_)=> print('callback1'))
    .then((_)=> print('callback2')); 

在task打印完毕以后,通过then串起来的回调函数会按照链接的顺序依次执行。 如果task执行出错怎么办?你可以通过catchError来链上一个错误处理函数:

 Future(()=> throw 'we have a problem')
      .then((_)=> print('callback1'))
      .then((_)=> print('callback2'))
      .catchError((error)=>print('$error')); 

上面这个Future执行时直接抛出一个异常,这个异常会被catchError捕捉到。类似于Java中的try/catch机制的catch代码块。运行后只会执行catchError里的代码。两个then中的代码都不会被执行。

既然有了类似Java的try/catch,那么Java中的finally也应该有吧。有的,那就是whenComplete:

Future(()=> throw 'we have a problem')
    .then((_)=> print('callback1'))
    .then((_)=> print('callback2'))
    .catchError((error)=>print('$error'))
    .whenComplete(()=> print('whenComplete')); 

无论这个Future是正常执行完毕还是抛出异常,whenComplete都一定会被执行。

以上就是对Future的一些主要用法的介绍。Future背后的实现机制还是有一些复杂的。这里先列几个来自Dart官网的关于Future的烧脑说明。大家先感受一下:

  1. 你通过then串起来的那些回调函数在Future完成的时候会被立即执 行,也就是说它们是同步执行,而不是被调度异步执行。
  2. 如果Future在调用then串起回调函数之前已经完成,
    那么这些回调函数会被调度到微任务队列异步执行。
  3. 通过Future()Future.delayed()实例化的Future不会同步执行,它们会被调度到事件队列异步执行。
  4. 通过Future.value()实例化的Future会被调度到微任务队列异步完成,类似于第2条。
  5. 通过Future.sync()实例化的Future会同步执行其入参函数,然后(除非这个入参函数返回一个Future)调度到微任务队列来完成自己,类似于第2条。

从上述说明可以得出结论,Future中的代码至少会有一部分被异步调度执行的,要么是其入参函数和回调被异步调度执行,要么就只有回调被异步调度执行。

不知道大家注意到没有,通过以上那些Future构造函数生成的Future对象其实控制权不在你这里。它什么时候执行完毕只能等系统调度了。你只能被动的等待Future执行完毕然后调用你设置的回调。如果你想手动控制某个Future怎么办呢?请使用Completer

Completer

这里就举个Completer的例子吧

// 实例化一个Completer
var completer = Completer();
// 这里可以拿到这个completer内部的Future
var future = completer.future;
// 需要的话串上回调函数。
future.then((value)=> print('$value'));

//做些其它事情 
...
// 设置为完成状态
completer.complete("done"); 

上述代码片段中,当你创建了一个Completer以后,其内部会包含一个Future。你可以在这个Future上通过then, catchErrorwhenComplete串上你需要的回调。拿着这个Completer实例,在你的代码里的合适位置,通过调用complete函数即可完成这个Completer对应的Future。控制权完全在你自己的代码手里。当然你也可以通过调用completeError来以异常的方式结束这个Future

总结就是:

  • 我创建的,完成了调我的回调就行了: 用 Future
  • 我创建的,得我来结束它: 用Completer

Future相对于调度回调函数来说,缓减了回调地狱的问题。但是如果Future要串起来的的东西比较多的话,代码还是会可读性比较差。特别是各种Future嵌套起来,是比较烧脑的。

所以能不能更给力一点呢?可以的!JavaScript有 async/await,Dart也有。

async/await

asyncawait是什么?它们是Dart语言的关键字,有了这两个关键字,可以让你用同步代码的形式写出异步代码。啥意思呢?看下面这个例子:

foo() async {
  print('foo E');
  String value = await bar();
  print('foo X $value');
}

bar() async {
  print("bar E");
  return "hello";
}

main() {
  print('main E');
  foo();
  print("main X");
} 

函数foo被关键字async修饰,其内部的有3行代码,看起来和普通的函数没什么两样。但是在第2行等号右侧有个await关键字,await的出现让看似会同步执行的代码裂变为两部分。如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DZXz6ckh-1630664306751)(https://user-gold-cdn.xitu.io/2019/1/29/16898e5f891d4d3f?imageView2/0/w/1280/h/960/ignore-error/1)]

绿框里面的代码会在foo函数被调用的时候同步执行,在遇到await的时候,会马上返回一个Future,剩下的红框里面的代码以then的方式链入这个Future被异步调度执行。

上述代码运行以后在终端会输出如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mOqcgiP6-1630664306753)(https://user-gold-cdn.xitu.io/2019/1/29/168990eabe692cff?imageView2/0/w/1280/h/960/ignore-error/1)]

可见print('foo X $value')是在main执行完毕以后才打印出来的。的确是异步执行的。

而以上代码中的foo函数可以以Future方式实现如下,两者是等效的

foo() {
  print('foo E');
  return Future.sync(bar).then((value) => print('foo X $value'));
## 最后

最后这里放上我这段时间复习的资料,这个资料也是偶然一位朋友分享给我的,里面包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

还有 **高级架构技术进阶脑图、高级进阶架构资料** 帮助大家学习提升进阶,也可以分享给身边好友一起学习。

**[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

![](https://img-blog.csdnimg.cn/img_convert/d06788ed9e7e367409198b4d47af9668.png)

![](https://img-blog.csdnimg.cn/img_convert/9ab8f0031be83a6fd9046c8ee1d869f8.png)

eChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**

[外链图片转存中...(img-E0PGxTSN-1630664306755)]

[外链图片转存中...(img-J3lo1naM-1630664306757)]

**一起互勉~**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值