[版权申明]非商业目的注明出处可自由转载
出自:shusheng007

概述

说实话,在使用了几个月的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的使用方法,如 thencatchErrorwhenComplete等。

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

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

Future.value('ShuSheng007')
    .then((value) => print(value))
    .catchError((e,stackTrace) =>print('Exception:$e StackTrace:$stackTrace'))
    .whenComplete(() => print('无论如何都执行'));

执行结果为:

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

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

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

第二种:async与await

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

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

Future<void> printUserName() async {
  String name = await getUserName();
  print('my name is :$name');
}

//模拟从网上获取数据
Future<String> getUserName() =>
    Future.delayed(Duration(seconds: 1), () => 'ShuSheng007');

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

 FlatButton(
   onPressed: () {
     printUserName();
     print('我写在打印名字下面却先执行了');
   },
   child: Text('Run'),
 )

输出结果:

I/flutter ( 4787): 开始打印名字
I/flutter ( 4787): 我写在打印名字下面却先执行了
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时就它就被暂停了,程序会接着执行此方法下面的代码,例如此处的

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

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

异常处理

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

    try {
      print('开始打印名字');
      String name = await getUserName();
      print('my name is :$name');
    } catch (e) {
      print(e);
    } finally {      
    }

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

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

并发执行任务

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

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

Future<void> printUserName() async {
  List<Object> results= await Future.wait([getUserName(),getUserAge()]);
  print('my name is :${results[0]},I am ${results[1]} years old');
}

Future<String> getUserName() =>
   Future.delayed(Duration(seconds: 1), () => 'ShuSheng007');
Future<int> getUserAge() =>
   Future.delayed(Duration(seconds: 2), () => 18);

输出结果:

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

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

Stream

敬请期待…

总结

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

来,少年!怒戳一个赞可否?

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐