Dart异步是怎么实现
Posted 一叶飘舟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dart异步是怎么实现相关的知识,希望对你有一定的参考价值。
前言
Dart 是单线程的,但通过事件循环可以实现异步。
而 Future 是异步任务的封装,借助于 await 与 async,我们可以通过事件循环实现非阻塞的同步等待;
Isolate 是 Dart 中的多线程,可以实现并发,有自己的事件循环与 Queue,独占资源。Isolate 之间可以通过消息机制进行单向通信,这些传递的消息通过对方的事件循环驱动对方进行异步处理。
事件循环
单线程模型中主要就是在维护着一个事件循环(Event Loop)。
在 Dart 中,实际上有两个队列,一个事件队列(Event Queue),另一个则是微任务队列(Microtask Queue)。在每一次事件循环中,Dart 总是先去第一个微任务队列中查询是否有可执行的任务,如果没有,才会处理后续的事件队列的流程。所以,Event Loop 完整版的流程图,应该如下所示:
在Dart的单线程中,代码到底是怎样执行的呢?
- 1、Dart的入口是main函数,所以
main函数中的代码
会优先执行; - 2、main函数执行完后,会启动一个事件循环(Event Loop)就会启动,启动后开始执行队列中的任务;
- 3、首先,会按照先进先出的顺序,执行
微任务队列(Microtask Queue)
中的所有任务; - 4、其次,会按照先进先出的顺序,执行
事件队列(Event Queue)
中的所有任务;
那么在Flutter开发中,哪些是放在事件队列,哪些是放在微任务队列呢?
- 所有的外部事件任务都在事件队列中,如IO、计时器、点击、以及绘制事件等;
- 而微任务通常来源于Dart内部,并且微任务非常少。这是因为如果微任务非常多,就会造成事件队列排不上队,会阻塞任务队列的执行(比如用户点击没有反应的情况);
- 事件队列包含外部事件,例如I/O, Timer,绘制事件,Future(部分情况)等等。
- 微任务队列则包含有Dart内部的微任务,主要是通过scheduleMicrotask,Future(部分情况)来调度。
复制代码
Future的代码是加入到事件队列还是微任务队列呢?
Future中通常有两个函数执行体:
- Future构造函数传入的函数体
- then的函数体(catchError等同看待)
那么它们是加入到什么队列中的呢?
- Future构造函数传入的函数体放在事件队列中
- then的函数体要分成三种情况:
- 情况一:Future没有执行完成(有任务需要执行),那么then会直接被添加到Future的函数执行体后;
- 情况二:如果Future执行完后就then,该then的函数体被放到如微任务队列,当前Future执行完后执行微任务队列;
- 情况三:如果Future是链式调用,意味着then未执行完,下一个then不会执行;
Future
Future 默认情况下其实就是往「事件队列」里插入一个事件,当有空余时间的时候就去执行,当执行完毕后会回调 Future.then(v) 方法。
而我们也可以通过使用 Future.microtask 方法来向 「微任务队列」中插入一个任务,这样就会提高他执行的效率。
Future
就是用来处理异步操作的,异步处理成功了就执行成功的操作,异步处理失败了就捕获错误或者停止后续操作。一个Future
只会对应一个结果,要么成功,要么失败;
需要注意的是,Future
的所有API
的返回值仍然是一个Future
对象,所以我们可以很方便的进行链式调用;
1. await/async
- 它们是Dart中的关键字
- 它们可以让我们用同步的代码格式,去实现异步的调用过程。
- 并且,通常一个async的函数会返回一个Future
Future<String> getNetworkData() async
var result = await Future.delayed(Duration(seconds: 3), ()
return "network data";
);
return "请求到的数据:" + result;
2. Future.then
在then中接收异步结果
main(List<String> args)
print("main function start");
// 使用变量接收getNetworkData返回的future
var future = getNetworkData();
// 当future实例有返回结果时,会自动回调then中传入的函数
// 该函数会被放入到事件循环中,被执行
future.then((value)
print(value);
).catchError((error)
// 捕获出现异常时的情况
print(error);
);
print(future);
print("main function end");
3. Future.catchError
如果异步任务发生错误,我们可以在catchError中捕获错误
4. Future.whenComplete
无论异步任务执行成功或失败都需要做一些事的场景,
比如在网络请求前弹出加载对话框,在请求结束后关闭对话框。这种场景,有两种方法,
第一种是分别在then或catch中关闭一下对话框,
第二种就是使用Future的whenComplete回调
未完成状态(uncompleted) 完成状态(completed)
完成状态(completed)
5. Future的链式调用
- 我们可以在then中继续返回值,会在下一个链式的then调用回调函数中拿到返回的结果
import "dart:io";
main(List<String> args)
print("main function start");
getNetworkData().then((value1)
print(value1);
return "content data2";
).then((value2)
print(value2);
return "message data3";
).then((value3)
print(value3);
);
print("main function end");
Future<String> getNetworkData()
return Future<String>(()
sleep(Duration(seconds: 3));
// 不再返回结果,而是出现异常
return "network data1";
);
6. Future.delayed(时间, 回调函数)
- 在延迟一定时间时执行回调函数,执行完回调函数后会执行then的回调;
main(List<String> args)
print("main function start");
Future.delayed(Duration(seconds: 3), ()
return "3秒后的信息";
).then((value)
print(value);
);
print("main function end");
复制代码
Future与Stream区别
1、Future 表示稍后获得的一个数据,所有异步的操作的返回值都用 Future 来表示。
2、Future 只能表示一次异步获得的数据。
3、Stream 表示多次异步获得的数据。比如界面上的按钮可能会被用户点击多次,按钮上的点击事件(onClick)就是一个 Stream 。
4、Future将返回一个值,而Stream将返回多次值。
5、Dart 中统一使用 Stream 处理异步事件流。
Isolate
Isolate 是 Dart 中的线程
-
比如Flutter中就有一个Root Isolate,负责运行Flutter的代码,比如UI渲染、用户交互等等;
-
Dart 中的线程是以隔离 (Isolate)的方式存在的,每个 Isolate 都有自己的 Event Loop 与 Queue
-
Isolate 之间不共享任何资源,只能依靠消息机制通信,不需要竞争资源,就不需要锁(不用担心死锁问题),因此也就没有资源抢占问题。
-
每个Isolate 都有自己独立的,私有内存块(多个线程不共享内存)
但是,如果只有一个Isolate,那么意味着我们只能永远利用一个线程,这对于多核CPU来说,是一种资源的浪费。
如果在开发中,我们有非常多耗时的计算,完全可以自己创建Isolate,在独立的Isolate中完成想要的计算操作。
什么时候使用Isolate
1、如果一段代码不会被中断,那么就直接使用正常的同步执行就行。
2、如果代码段可以独立运行而不会影响应用程序的流畅性,建议使用 Future (需要花费几毫秒时间)
3、如果繁重的处理可能要花一些时间才能完成,而且会影响应用程序的流畅性,建议使用 isolate (需要几百毫秒)
下面列出一些使用 isolate 的具体场景:
1、JSON解析: 解码JSON,这是HttpRequest的结果,可能需要一些时间,可以使用封装好的 isolate 的 compute 顶层方法。
2、加解密: 加解密过程比较耗时
3、图片处理: 比如裁剪图片比较耗时
4、从网络中加载大图
如何创建Isolate呢?
Isolate通信机制
但是在真实开发中,我们不会只是简单的开启一个新的Isolate,而不关心它的运行结果:
- 我们需要新的Isolate进行计算,并且将计算结果告知Main Isolate(也就是默认开启的Isolate);
- Isolate 通过发送管道(SendPort)实现消息通信机制;
- 我们可以在启动并发Isolate时将Main Isolate的发送管道作为参数传递给它;
- 并发在执行完毕时,可以利用这个管道给Main Isolate发送消息;
import "dart:isolate";
main(List<String> args) async
// 1.创建管道
ReceivePort receivePort= ReceivePort();
// 2.创建新的Isolate
Isolate isolate = await Isolate.spawn<SendPort>(foo, receivePort.sendPort);
// 3.监听管道消息
receivePort.listen((data)
print('Data:$data');
// 不再使用时,我们会关闭管道
receivePort.close();
// 需要将isolate杀死
isolate?.kill(priority: Isolate.immediate);
);
void foo(SendPort sendPort)
sendPort.send("Hello World");
以上是关于Dart异步是怎么实现的主要内容,如果未能解决你的问题,请参考以下文章