[TOC]

​ dart是一种单线程语言,异步模型主要是通过事件轮询(event loop)来实现,另外也提供了更高级的Isolate来支持多线程,通常用于计算比较耗时的操作。

# Event loop

​ dart中的事件轮询包含两种事件队列:MicroTaskEventTask,其中经常使用的属于EventTask队列,MicroTask并不常用,也不推荐使用。

dart中的事件轮询看起来像这样:(摘自Flutter异步编程)

void eventLoop(){
    while (microTaskQueue.isNotEmpty){
     //执行MicroTask队列
    }
    if (eventQueue.isNotEmpty){
     //执行Event队列
    }
}
1
2
3
4
5
6
7
8
9

从上面代码可以看出来,每次事件轮询总是先执行完MicroTask中的事件。

# MicroTask(不推荐使用)

创建MicroTask有两种方法:

//第一种:使用全局静态方法创建
scheduleMicrotask((){
    print('this is a MicroTask demo');
});
//第二种:使用Future.microTask方法创建
 var result=await Future.microtask((){
    return "this is a MicroTask demo";
  });
  print(result);
1
2
3
4
5
6
7
8
9
10

​ 从使用API上可以看出使用Future.microtask的好处是允许我们有一个返回值(内部通过Future包裹实现),而scheduleMicrotask则不支持返回值。

# EventTask

​ 这是一种常用的事件队列,比如await/async,Timer,Future,Stream等,除了MicroTask之外的所有事件。

# await/async

async标记一个方法将要返回一个Future对象,该对象是可以被await的,dart中异步的一个重要标识就是await,每当遇到一个await时,dart都会等待awaitFuture执行完成后再执行后面的代码。

await/async只是用来简化Future的语法糖而已。

//如果你希望一个方法时异步执行的,首先它要有一个async标记
Future foot(int index) async{
    await Future.delayed(Duration(seconds: 1));
    print(index);
}
void main() async{
    print(1);
    //await可以让这个代码同步执行
    await foot(2);
    print(3);
    //没有await标记,代码将异步执行
    foot(4);
    print(5);
}
//下面时执行结果,是否符合你的预期:
1
2
3
5
4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Timer定时器

​ Timer是dart中的定时器,支持立即(Timer.run()),延迟(Time()),间隔(Timer.periodic())3种执行方式。

​ 因为dart是单线程运行的所以Timer的执行也是需要在EventTask队列中排队执行的。下面这段代码演示Timer也要写入EventTask队列中才能运行:

import 'dart:async';
void main() async {
  //立即执行
  Timer.run(() {
    print('timer 0');
  });
  //延迟2s执行
  Timer(Duration(seconds: 2), () {
    print('timer 2');
  });
  var begin = DateTime.now();
  //使用循环延迟1s,
  //使用这种方式的好处是,一旦循环开始必须结束才会执行其它操作
  while (true) {
    var microseconds = DateTime.now().difference(begin).inMicroseconds;
    if (microseconds > 1000) {
      print('timer');
      break;
    }
  }
  await Future.delayed(Duration(seconds: 1));
  //延迟1s
  Timer(Duration(seconds:1),(){
    print(('timer 1'));
  });
}
//执行结果:
timer
timer 0
timer 1
timer 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

代码开头我们定义了一个立即执行Timer,但是先执行的却是while代码块,说明Timer只是向事件循环中添加了一个任务,while代码块延迟1s,所以先被添加到事件循环的就是延迟1s的Timer,这也说明只有到了延迟时间才会将后续要执行的代码放进事件循环,而不是在定义的时候就放进去的。

# Future

FutureTimer的加强版本,一个Timer通常只处理一个无返回值的函数,Future对Timer进行了包装。Future是一个异步处理对象,所有的异步操作都返回一个Future对象,Future不是最终的返回值,只是一个异步状态值,你可以对一个Future对象使用await来等待异步操作完成。

Future()

//使用Future可以很容易的创建一个异步运行的匿名方法
var result=await Future((){
    print('返回一个bool值');
    return true;
});
print(result);
1
2
3
4
5
6

Future.delayed()

//延迟异步执行一个方法
result=await Future.delayed(Duration(seconds: 1),(){
    print('延迟返回一个bool值');
    return true;
});
print(result);
1
2
3
4
5
6

Future.wait()

//等待异步方法完成,wait可以保证所有的future都是按照顺序执行
//相当于:
// await future1;
// await future2;
// await future3;
var future1=Future.delayed(Duration(seconds: 5),(){
    return '第1个Future完成';
});
var future2=Future((){
    return '第2个Future完成';
});
var future3=Future.delayed(Duration(seconds: 1),(){
    return '第3个Future完成';
});
print('等待Futures完成');
var results =await Future.wait([future1,future2,future3]);
for (var item in results) {
    print(item);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

Future.any()

//当任何一个future完成时结束
var anyFuture1=Future.delayed(Duration(seconds: 5),(){
    return '第一个Future';
});
var anyFuture2=Future((){
    return '第二个Future';
});
var anyFuture3=Future.delayed(Duration(seconds: 2),(){
    return '第3个Future';
});
print('等待最快完成的Future');
var item = await Future.any([anyFuture1,anyFuture2,anyFuture3]);
print(item);
1
2
3
4
5
6
7
8
9
10
11
12
13

Future.forEach()

//使用Future遍历Iterable<T>对象,
//下面对比普通的forEach与Future.forEach的区别
[3,2,1].forEach((item) async{
    await Future.delayed(Duration(seconds: item));
    print('forEach item $item');
});
//Future.forEach总是按顺序执行
await Future.forEach([3,2,1], (item) async{
    await Future.delayed(Duration(seconds: 1));
    print('Future.forEach item $item');
});
1
2
3
4
5
6
7
8
9
10
11

Future.doWhile()

//启动一个Future,直到返回值为false时结束
var index=0;
await Future.doWhile((){
    print('doWhile item ${index++}');
    return index!=10;
});
1
2
3
4
5
6

Future.sync

//直接执行sync中传递的方法,始终返回一个Future
//下面的代码可以看到返回结果始终时一个Future
var syncResult1=await Future.sync((){
    return "无async标记";
});
print(syncResult1);
var syncResult2=await Future.sync(() async{
    return '有async标记';
});
print(syncResult2);
1
2
3
4
5
6
7
8
9
10

Future.microtask()

//创建一个MicroTask任务
Future.microtask((){});
1
2

Future.value()

//从值对象构造一个Future
var future=await (){
    //等价:Future.sync(()=>'future执行结果');
    return Future.value('future执行结果');
}();
print(future);
1
2
3
4
5
6

Future.error()

//创建一个错误返回值的Future
var failedFuture=await (){
    	return Future.error('创建一个错误返回值的Future');
    }();
    print(failedFuture);
}
1
2
3
4
5
6

# Stream

下面这张图演示了Stream的运行原理:

# MicroTask与EventTask的执行顺序对比:

下面有两段官方提供的代码足以搞懂他们的区别:

  1. https://dart.dev/articles/archive/event-loop#question-1
  2. https://dart.dev/articles/archive/event-loop#question-2

# Isolate

dart是一个单线程程序,在执行耗时的操作是会导致线程卡住,尤其在Flutter上会导致ui卡顿。

isolate优点是将耗时的代码放在一个独立的线程中执行,缺点是不能共享其它线程的实例成员,有点类似进程间的数据隔离。

import 'dart:async';
import 'dart:io';
import 'dart:isolate';
computer(SendPort port) async {
  var receivePort = ReceivePort();
  // 向client暴漏自己的可通信对象
  port.send(receivePort.sendPort);
  Future(() async {
    //监听client消息
    await for (var data in receivePort) {
      print(data);
	  await Future.delayed(Duration(seconds: 1));
      port.send(++data);
    }
  });
}
main(List<String> args) async {
  var receivePort = ReceivePort();
  // isolate要求第一个参数不能是匿名方法
  // 如果将第一个参数作为server端,那么第二参数就是客户端,server端通过这个参数来与client端交互
  await Isolate.spawn(computer, receivePort.sendPort);
  var stream = receivePort.asBroadcastStream();
  // 接收server端的可通信对象,用于发送数据
  SendPort sendPort = await stream.first;
  // 发送一个数据
  sendPort.send(0);
  Future(() async {
    // 监听收到的server端消息
    await for (var data in stream) {
      print(data);      
      await Future.delayed(Duration(seconds: 1));        
      sendPort.send(++data);
    }
  });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38