flutter异步编程-事件循环、Isolate、Stream(流)

事件循环、Isolate

开始前我们需要明白 Dart 是单线程的并且 Flutter 依赖于 Dart

如果你知道js 中的event loop 将很好理解dart的整个异步过程

先看一段代码


import 'dart:async';

Future eventLoop() async{

print('A');

Future((){

print('F');

scheduleMicrotask((){print('H');});

Future((){

print('M');

}).then((_){

print('N');

});

}).then((_){

print('G');

});

Future((){

print('I');

}).then((_){

print('J');

});

scheduleMicrotask(text1);

scheduleMicrotask((){print('D');});

print('B');

}

void text1() {

print('C');

scheduleMicrotask((){print('E');});

Future((){

print('K');

}).then((_){

print('L');

});

}

你只到输出结果吗

正确的输出顺序是: A B C D E F G H I J K L M N

eventLoop

1、MicroTask 队列

微任务队列,一般使用scheduleMicroTask方法向队列中添加

这是大多数时候你不必使用的东西。比如,在整个 Flutter 源代码中 scheduleMicroTask() 方法仅被引用了 7 次, 所以最好优先考虑使用 Event 队列

2、Event 队列

I/O、手势、绘图、计时器、流、futures等等异步操作都将进入event队列

尽量使用事件队列可以使微任务队列更短,降低事件队列卡死的可能性

代码执行顺序

首先我们知道dart是单线程的,所以dart的代码执行顺序是:

同步代码依次执行 碰到异步代码先进对应的队列中,然后继续执行下面的代码 当同步代码执行完毕,先去看MicroTask 队列中的任务,将MicroTask队列中的任务依次执行完毕 MicroTask中的任务执行完毕后,再去看Event 队列中的任务,event队列出一个任务 然后执行 , 然后回到第三步 循环 直到所有队列都清空

Isolate

Isolate 是 Dart 中的 线程, Flutter的代码都是默认跑在root isolate上的

「Isolate」在 Flutter 中并不共享内存。不同「Isolate」之间通过「消息」进行通信。

import 'dart:async';

import 'dart:io';

import 'dart:isolate';

import 'package:flutter/foundation.dart';

import 'package:flutter/material.dart';

//一个普普通通的Flutter应用的入口

//main函数这里有async关键字,是因为创建的isolate是异步的

void main() async{

runApp(MyApp());

//asyncFibonacci函数里会创建一个isolate,并返回运行结果

print(await asyncFibonacci(20));

}

//这里以计算斐波那契数列为例,返回的值是Future,因为是异步的

Future<dynamic> asyncFibonacci(int n) async{

//首先创建一个ReceivePort,为什么要创建这个?

//因为创建isolate所需的参数,必须要有SendPort,SendPort需要ReceivePort来创建

final response = new ReceivePort();

//开始创建isolate,Isolate.spawn函数是isolate.dart里的代码,_isolate是我们自己实现的函数

//_isolate是创建isolate必须要的参数。

await Isolate.spawn(_isolate,response.sendPort);

//获取sendPort来发送数据

final sendPort = await response.first as SendPort;

//接收消息的ReceivePort

final answer = new ReceivePort();

//发送数据

sendPort.send([n,answer.sendPort]);

//获得数据并返回

return answer.first;

}

//创建isolate必须要的参数

void _isolate(SendPort initialReplyTo){

final port = new ReceivePort();

//绑定

initialReplyTo.send(port.sendPort);

//监听

port.listen((message){

//获取数据并解析

final data = message[0] as int;

final send = message[1] as SendPort;

//返回结果

send.send(syncFibonacci(data));

});

}

int syncFibonacci(int n){

return n < 2 ? n : syncFibonacci(n-2) + syncFibonacci(n-1);

}

因为Root isolate会负责渲染,还有UI交互,如果我们有一个很耗时的操作呢?前面知道isolate里是一个event loop(事件循环),如果一个很耗时的task一直在运行,那么后面的UI操作都被阻塞了,所以如果我们有耗时的操作,就应该放在isolate里!

Stream(流)

什么是流?

这个大机器就是StreamController,它是创建流的方式之一。 StreamController有一个入口,叫做sink sink可以使用add方法放东西进来,放进去以后就不再关心了。 当有东西从sink进来以后,我们的机器就开始工作 StreamController有一个出口,叫做stream 机器处理完毕后就会把产品从出口丢出来,但是我们并不知道什么时候会出来,所以我们需要使用listen方法一直监听这个出口 而且当多个物品被放进来了之后,它不会打乱顺序,而是先入先出

使用Stream


StreamController controller = StreamController();

//监听这个流的出口,当有data流出时,打印这个data

StreamSubscription subscription =

controller.stream.listen((data)=>print("$data"));

controller.sink.add(123);

// 输出: 123

你需要将一个方法交给stream的listen函数,这个方法入参(data)是我们的StreamController处理完毕后产生的结果,我们监听出口,并获得了这个结果(data)。这里可以使用lambda表达式,也可以是其他任何函数

transform

如果你需要更多的控制转换,那么请使用transform()方法。他需要配合StreamTransformer进行使用。


StreamController<int> controller = StreamController<int>();

final transformer = StreamTransformer<int,String>.fromHandlers(

handleData:(value, sink){

if(value==100){

sink.add("你猜对了");

}

else{ sink.addError('还没猜中,再试一次吧');

}

});

controller.stream

.transform(transformer)

.listen(

(data) => print(data),

onError:(err) => print(err));

controller.sink.add(23);

//controller.sink.add(100);

// 输出: 还没猜中,再试一次吧

StreamTransformer<S,T>是我们stream的检查员,他负责接收stream通过的信息,然后进行处理返回一条新的流。

S代表之前的流的输入类型,我们这里是输入一个数字,所以是int。 T代表转化后流的输入类型,我们这里add进去的是一串字符串,所以是String。 handleData接收一个value并创建一条新的流并暴露sink,我们可以在这里对流进行转化

Stream的种类

"Single-subscription" streams 单订阅流 "broadcast" streams 多订阅流

单订阅流


StreamController controller = StreamController();

controller.stream.listen((data)=> print(data));

controller.stream.listen((data)=> print(data));

controller.sink.add(123);

// 输出: Bad state: Stream has already been listened to. 单订阅流不能有多个收听者。

单个订阅流在流的整个生命周期内仅允许有一个listener

多订阅流


StreamController controller = StreamController();

//将单订阅流转化为广播流

Stream stream = controller.stream.asBroadcastStream();

stream.listen((data)=> print(data));

stream.listen((data)=> print(data));

controller.sink.add(123);

// 输出: 123 123

广播流允许任意数量的收听者,且无论是否有收听者,他都能产生事件。所以中途进来的收听者将不会收到之前的消息。

参考 深入了解isolate
永久原文地址 flutter异步编程