在java中,有Thread来代表一个线程,但是在dart中是没有线程概念的,只有类似于多线程的isolate。除此之外,还针对读文件、数组操作专门制定了一个异步Stream类,使用起来很方便。本次主要分享一下dart中是如何进行异步操作的,这里跟java差别还是蛮大的。

一、isolate

Dart是基于单线程模型的语言。但是在开发当中我们经常会进行耗时操作比如网络请求,这种耗时操作会堵塞我们的代码,所以在Dart也有并发机制,名叫isolate。APP的启动入口main函数就是一个类似Android主线程的一个主isolate。和Java的Thread不同的是,Dart中的isolate无法共享内存,类似于Android中的多进程。

多说无益,直接上代码。如下图,我们首先在主isolate中定义一个receivePort,然后将该receivePort中的sendPort对象以及新的isolate入口方法isolateMain传给了Isolate,相当于告诉新的isolate,你接下来执行这个isolateMain方法吧,和我的通信方式是receivePort.sendPort。接下来再看看isolateMain方法,isolateMain方法中也new了一个receivePort对象传给了主isolate,也是告诉了主线程和它的通信方式是通过这个sendPort来发送信息。具体的发送方式很简单,就是sendPort.send(msg)即可。接收消息是通过刚刚new出来的receivePort.listen()的方式,listen方法的回调参数是一个匿名方法,返回值就是其他isolate传过来的数据。

需要注意的是,由于在不同的isolate中是不共享内存的,这个和Android中的进程有点类似,所以当前isolate中的变量在其他isolate中是不可见的。在本例中,我们在子isolate的入口函数中打印出子isolate中的i变量的值,结果为null,是因为i只在主isolate中赋值为10了,但是子isolate中并没有赋值,也就是null了。那么应该如何让主isolate中的改动让子isolate可见呢,只需要在主isolate中发送一条消息让子isolate修改即可。另外,如果在全局范围内改也是对所有isolate生效的。

二、event-loop

我们首先看一个例子,在单个isolate中由于是在单线程中,所以顺序只能有一个并不能并行。如下例,我们在主isolate中首先注册了监听,然后睡眠了2秒,重点来了,就算在2秒内收到了消息,但是还是会等到那2秒睡眠时间过了以后才会执行收到的消息。最后的结果是首先输出null,然后2秒后再输入休眠完成,然后马上跟上了那2条消息。

同Android Handler类似,在Dart运行环境中也是靠事件驱动的,通过event loop不停的从队列中获取消息或者事件来驱动整个应用的运行,isolate发过来的消息就是通过loop处理。但是不同的是在Android中每个线程只有一个Looper所对应的MessageQueue,而Dart中有两个队列,一个叫做event queue(事件队列),另一个叫做microtask queue(微任务队列)。其中上面的例子中使用的是事件队列,事件队列的优先级不如微任务队列。

Dart在执行完main函数后,才会由Loop开始执行两个任务队列中的Event,这就是main方法内sleep会影响回调执行的原因。首先Loop检查微服务队列,依次执行Event,当微服务队列执行完后,就检查Event queue队列依次执行,在执行Event queue的过程中,每执行完一个Event就再检查一次微服务队列。所以微服务队列优先级高,可以利用微服务进行插队。对于这个特性,我们可以再看一个例子。在该例子中,主isolate中有一个死循环,所以导致loop并不会去检查事件队列,所以这个输出永远都出不来,将一直卡在main()中。

我们再来验证一下微服务的插队功能,如下例,首先调用then方法将读文件加入到事件队列,然后开启了一个微服务。在这里执行的顺序是main->微服务->事件队列,所以结果是先输出future:excute microtask,然后输出被读文件中的内容。

三、future

在 Dart 库中随处可见 Future 对象,通常异步函数返回的对象就是一个 Future。 当一个 future 执行完后,他里面的值 就可以使用了,可以使用 then() 来在 future 完成的时候执行其他代码。Future对象其实就代表了在事件队列中的一个事件的结果。

可能看这个说明有点晕乎,我们看一下上面读文件那个例子中的then方法返回值吧,看到没返回值就是Future对象。也就是说事件队列的结果就是一个future,这里之所以拿出来讲是因为dart中使用到的地方实在太多了。接下来一起看看future类中都提供了哪些功能吧。

1.异常捕获

通过future类可以捕获到时间队列中的错误,通过catchError方法来获取到回调。我们知道在主Isolate中捕获异常可以用try...catch,其实catchError可以看做是异步的try...catch。

2.组合

then()的返回值同样是一个future对象,可以利用队列的原理进行组合异步任务。在本例中,先执行文件读取操作,然后再将返回值1进行输出,最后抓一下错误。相当于类似build模式,then可以一直点下去,上次的返回值就是本次的参数。

上面是一个一个任务执行,还有一种操作是等待多个任务都完成以后再执行其他操作。如下例,我们定义了3个任务,第一个是读文件,第二个是延迟3秒,第三个是输出一些内容。由于使用了Future.wait方法将前2个任务绑定,所以结果是会等前2个任务完成以后才会执行第三个

四、Stream

Future 表示稍后获得的一个数据,所有异步的操作的返回值都用 Future 来表示。但是 Future 只能表示一次异步获得的数据。而 Stream 表示多次异步获得的数据。比如 IO 处理的时候,每次只会读取一部分数据和一次性读取整个文件的内容相比,Stream 的好处是处理过程中内存占用较小。而 File 的readAsString()是一次性读取整个文件的内容进来,虽然获得完整内容处理起来比较方便,但是如果文件很大的话就会导致内存占用过大的问题。

补充个例子可能更加具体,使用openRead来打开Stream流,然后监听流的变化,最后执行了多次的回调。当然读取的文件要足够大,如果小的话也只会读取一次的。接下来继续使用future,以此来读取,两种方式都是可以的。

还有一个特性是可以使用onData来对监听进行重置。如下例,获取到监听者对象后,对监听方法进行了替换,最后执行的是新方法,而老方法直接被丢弃。除了替换监听方法,还可以执行其他的方法,比如调用onDone来结束一个监听。

五、广播

Stream有两种订阅模式:单订阅和多订阅。单订阅就是只能有一个订阅者,上面的使用我们都是单订阅模式,而广播是可以有多个订阅者。通过 Stream.asBroadcastStream() 可以将一个单订阅模式的 Stream 转换成一个多订阅模式的 Stream,isBroadcast 属性可以判断当前 Stream 所处的模式。

如下例,通过将asBroadcastStream将Stream转换成广播,然后就可以多处监听了。

需要注意的是,如果是直接创建的流管理器,就算是多订阅模式下,如果先发送然后再注册,是不会接收到消息的。

但是如果是通过asBroadcastStream来获取到streamCOntroller,先发送再注册,也是会接收到消息的。利用这个特性,可以用来实现sticky粘性广播。

六、async/await

使用'async'和'await'的代码是异步的,但是看起来很像同步代码。当我们需要获得A的结果,再执行B,时,你需要'then()->then()',但是利用'async'与'await'能够非常好的解决回调地狱的问题。

在下例中,我们将readFile用async进行修饰,代表该方法将使用同步关键字await。接下来我们读取了2次文件,都用await修饰,代表这两次操作都以同步的方式运行,只有在第一行执行完以后才进入第二行代码的执行。

最后,给大家带来使用stream手撸一个eventbus的代码。在注册时将Stream传出到调用处,然后就可以调用Stream的listen方法来获取消息。发送消息只需要调用StreamController的add方法即可,由于考虑到需要往不同的事件中发送消息,所以用了一个map来保存所有的订阅类型。另外,为了方便调用,这里使用了一个单例模式,对外公开了getInstance方法用来将单例对象传递给外界。取消注册是先关闭需要取消订阅的streamcontroller,然后从map移除

总结:本次介绍了异步操作isolate的使用,以及dart中事件队列、微服务队列的相关知识,然后了解了future对象是事件队列的结果,接下来对比了stream和future的区别,然后介绍了广播的直接创建、stream创建方式以及区别,接下来又介绍了async、await的结合使用方法,最后分享了自己的一个异步实战-使用Stream手写Eventbus。

dart基础之异步编程相关推荐

  1. dart系列之:dart中的异步编程

    文章目录 简介 为什么要用异步编程 怎么使用 Future 异步异常处理 在同步函数中调用异步函数 总结 简介 熟悉javascript的朋友应该知道,在ES6中引入了await和async的语法,可 ...

  2. [NodeJS]Node异步编程基础

    零.前言 为什么要用Node? Node把非阻塞IO作为提高应用性能的方式.而在JS中,天生拥有着异步编程机制: 事件机制.同时JS中不存在多进程.这样当你执行相对较慢需要花费时间长的IO操作时并不会 ...

  3. java socket 异步回调函数,分享nodejs异步编程基础之回调函数用法

    nodejs异步编程基础之回调函数用法分析 本文实例讲述了nodejs异步编程基础之回调函数用法.分享给大家供大家参考,具体如下: Node.js 异步编程的直接体现就是回调. 异步编程依托于回调来实 ...

  4. async And await异步编程活用基础

    async And await异步编程活用基础 原文:async And await异步编程活用基础 好久没写博客了,时隔5个月,奉上一篇精心准备的文章,希望大家能有所收获,对async 和 awai ...

  5. dart异步编程_如何通过期货将一些异步编程引入Dart

    dart异步编程 by Mohammed Salman 穆罕默德·萨尔曼(Mohammed Salman) 如何通过期货将一些异步编程引入Dart (How to bring a little asy ...

  6. C#基础--异步编程

    文章目录 异步编程 历史 同步调用 异步模式 基于事件的异步模式 基于任务的异步模式 异步编程基础 创建任务 调用异步方法 使用 Awaiter 延续任务 同步上下文 使用多个异步方法 按顺序调用异步 ...

  7. C#异步编程基础入门总结

    给.neter们整理了一份<.NET/C#面试手册>,目前大约4万字左右,初衷也很简单,就是希望在面试的时候能够帮助到大家,减轻大家的负担和节省时间.对于没有跳槽打算的也可以复习一下相关知 ...

  8. 第一章: Vert.x 异步编程的基础知识

    第一章: Vert.x 异步编程的基础知识 翻译: 白石(https://github.com/wjw465150/Vert.x-Core-Manual) 构建反应式系统的第一步是采用异步编程.基于阻 ...

  9. Boost.Asio基础(五) 异步编程初探

    异步编程 本节深入讨论异步编程将遇到的若干问题.建议多次阅读,以便吃透这一节的内容,这一节是对整个boost.asio来说是非常重要的. 为什么须要异步 如前所述,通常同步编程要比异步编程更简单.同步 ...

最新文章

  1. 用Python爬一爬那些年你硬盘存过的老师们,太方便啦!
  2. AndroidStudio项目打包成jar
  3. 深入探索JVM自动资源管理
  4. MFC中CArray类原理及其应用
  5. 熟悉Linux的环境实验报告,实验1 熟悉Linux开发环境 实验报告
  6. html 加入li的作用,HTML的li有什么作用?
  7. java 配置文件加载_Java加载配置文件类
  8. 重载和重写以及重写的权限问题
  9. 手机游戏赚钱到底有多难?接入运营商或需一年
  10. 页面置换算法——最佳置换算法、最近最少使用算法、先进先出算法、时钟置换算法
  11. 浙大 PAT b1039
  12. 使用 Autofill 插件快速提交BUG
  13. 【设计模式学习笔记】之 开山篇
  14. JS学习总结(12)——Math对象/时间对象
  15. 分享Android开发中用到的图标icon设计下载地址
  16. VMware虚拟机中Linux系统如何修改IP地址
  17. phpQuery - PHP 处理 HTML DOM 的好帮手
  18. hdu4556(欧拉函数)
  19. 启用NVI的NAT的配置示例
  20. Excel实战 第1章 数据处理

热门文章

  1. 《深度学习课程》-吴立德教授-复旦大学
  2. Java-note-调试小技巧
  3. shell脚本定义日志输出函数
  4. 在线邮箱地址提取工具
  5. 程序安装mysql数据库,安装mysql数据库
  6. Linux基本命令(三)-----Linux常用命令(按功能分类)
  7. OpenSSL生成root CA及签发证书
  8. 对研发团队稳定性的思考
  9. 【mysql】用navicat连接虚拟机mysql出现错误代码(10038)
  10. CentOs中mysql的安装与配置