vlambda博客
学习文章列表

dart异步编程(async, await)吊打Java?

概述

说实话,在使用了几个月的Flutter后,我爱上了它,至少爱上了dart处理网络请求的方式。今天我想叨逼叨一下Flutter中异步编程这块

异步编程

异步编程是个古老的话题,感兴趣的同学请移步:维基百科。经过多年的发展,目前主要有如下几种解决方案:

  • Threading 多线程

  • Callbacks 回调

  • Futures,Promises

  • Reactive Extensions Rx系列, 例如rxjava

  • Coroutines 协程

若对协程有兴趣,请查看我的另一篇文章:秒懂Kotlin之小白都看的懂的协程教程(part1)

dart 采用了第三种方式:Futures。由于Dart的单线程模型,所以使用比Java简单很多,我敢保证Flutter绝对比原生开发简单,没试过的同学建议大胆尝试一下,毕竟IT这个行当就是要不断的折腾。

Future

Dart 使用Future来处理异步编程。顾名思义,它是一个未来才能获得结果的东东,所以如果某个操作是耗时的,你可以将这个耗时操作包装成一个Future,等未来某个时间点操作完成后,你就可以从这个Future里面取出结果了。

Dart 的Future前后支持两套使用方法。在1.9之前使用Future API,1.9之后加入了 Asynchrony support(async, await)。现在官方强烈推荐使用async与await这种方法,本文我们着重聊第二种方法,对于第一种方法浅尝辄止。

第一种:Future API 和 callbacks

这种方法的缺点有:

  • 使用回调,所以仍然需要以异步的方式写代码

  • 需要学习各种API的使用方法,如 then、 catchError、 whenComplete等。

虽然各种API方法的名称已经做到顾名思义了,但要熟练掌握仍然有一定的学习成本。

我们简单看一个使用实例:

 
   
   
 
  1. Future.value('ShuSheng007')

  2. .then((value) => print(value))

  3. .catchError((e,stackTrace) =>print('Exception:$e StackTrace:$stackTrace'))

  4. .whenComplete(() => print('无论如何都执行'));

执行结果为:

 
   
   
 
  1. I/flutter (16437): ShuSheng007

  2. I/flutter (16437): 无论如何都执行

一般的使用,上面的方法就够了。如果Future 内部逻辑执行正常,就会在 then方法中获得结果,如果期间Future或者 then中发生了异常就会进入 catchError内,最后无论成功失败,都会执行 whenComplete方法。

值得注意的是,上面3个方法可以一不同的顺序,不同的个数组合,所以也很灵活。

第二种:async与await

这个就牛逼了,用同步的方式写异步,还不用学习新的API,关键是你写的代码很容易理解,那附带的结果就是更容易维护。与kotlin协程有点像,但是比协程简单。

例如我有一个从网络获取姓名并打印的需求

 
   
   
 
  1. Future<void> printUserName() async {

  2. String name = await getUserName();

  3. print('my name is :$name');

  4. }


  5. //模拟从网上获取数据

  6. Future<String> getUserName() =>

  7. Future.delayed(Duration(seconds: 1), () => 'ShuSheng007');

当点击UI上的一个按钮时调用 printUserName方法。

 
   
   
 
  1. FlatButton(

  2. onPressed: () {

  3. printUserName();

  4. print('我写在打印名字下面却先执行了');

  5. },

  6. child: Text('Run'),

  7. )

输出结果:

 
   
   
 
  1. I/flutter ( 4787): 开始打印名字

  2. I/flutter ( 4787): 我写在打印名字下面却先执行了

  3. I/flutter ( 4787): my name is :ShuSheng007

我们可以看到,在Flutter中完成耗时任务非常简单,如果在Android中就需要使用RxJava等方式进行线程的切换。

如何使用

  1. 在方法的 {}前面加上 async关键字

  2. 使用了 async关键字的方法返回类型是一个 Future<T>,T为此方法具体的返回值类型,如果没有返回则为 void,即 Future<void>

  3. 在 {}就可以使用 await关键字来从 Future中获取到值了

当 printUserName 方法被调用时,当没有遇到 await 时正常执行,当遇到 await时就它就被暂停了,程序会接着执行此方法下面的代码,例如此处的

 
   
   
 
  1. print('我写在打印名字下面却先执行了');

当await那个Future执行完毕, printUserName 方法就会被唤起,接着执行下面的代码。

异常处理

异步支持使得异步编程的异常处理也变的非常简单,只要加上 await关键字,就与处理同步方法完全一样了

 
   
   
 
  1. try {

  2. print('开始打印名字');

  3. String name = await getUserName();

  4. print('my name is :$name');

  5. } catch (e) {

  6. print(e);

  7. } finally {

  8. }

只要在try 块里发生的异常都可以被捕获到,如果 getUserName()发生了未捕获的异常也可以在catch中捕获到,是不是与同步无异呢?但是一定要注意,想要捕获 getUserName()里的异常,必须使用 await关键字,即使你不关心其返回结果,但是要捕获异常,也必须要有 await

关于dart的异常处理传送门:秒懂Flutter之Dart中如何处理异常(Exception)

并发执行任务

有时我们需要同时请求多个互不相关的接口,然后等所有接口的值都返回后再展示页面,在Android原生中,我们可以使用RxJava的 zip操作符,那在dart中怎么办呢?

可以使用 Future.wait方法,如下所示:

 
   
   
 
  1. Future<void> printUserName() async {

  2. List<Object> results= await Future.wait([getUserName(),getUserAge()]);

  3. print('my name is :${results[0]},I am ${results[1]} years old');

  4. }


  5. Future<String> getUserName() =>

  6. Future.delayed(Duration(seconds: 1), () => 'ShuSheng007');

  7. Future<int> getUserAge() =>

  8. Future.delayed(Duration(seconds: 2), () => 18);

输出结果:

 
   
   
 
  1. I/flutter ( 4787): my name is :ShuSheng007,I am 18 years old

此方法在大约2秒后返回了结果。

Stream

敬请期待...

总结

写了一段时间Flutter,觉得Java真的是太严格了,虽然用的不爽,不过我还是很支持Java语法的严谨性,毕竟只有这样才能造就庞大的工程项目,小伙伴你们觉得Java和Dart哪个好用呢?

来,少年!怒戳一个赞可否?点个在看,少个bug!