Dart中的事件循环
- 一个Dart应用中有一个消息循环和两个队列,一个是event队列,一个是microtask队列。
- 优先级问题:microtask队列的优先级是最高的,当所有microtask队列执行完之后才会从event队列中读取事件进行执行。
- microtask队列中的event,
event队列包含所有的外来事件:I/O,mouse events,drawing events,timers,isolate之间的message等。
- 一般Future创建的事件是属于event队列的(利用Future.microtask方法例外),所以创建一个Future后,会插入到event队列中,顺序执行。
在 Dart 中,实际上有两个队列,一个事件队列(Event Queue),另一个则是微任务队列(Microtask Queue)。在每一次事件循环中,Dart 总是先去第一个微任务队列中查询是否有可执行的任务,如果没有,才会处理后续的事件队列的流程。

微任务顾名思义,表示一个短时间内就会完成的异步任务。从上面的流程图可以看到,微任务队列在事件循环中的优先级是最高的,只要队列中还有任务,就可以一直霸占着事件循环。
异步任务我们用的最多的还是优先级更低的 Event Queue。比如,I/O、绘制、定时器这些异步事件,都是通过事件队列驱动主线程执行的。
Dart 为 Event Queue 的任务建立提供了一层封装,叫作 Future
Future(() => print('Running in Future 1'));// 下一个事件循环输出字符串
Future(() => print(‘Running in Future 2'))
.then((_) => print('and then 1'))
.then((_) => print('and then 2’));// 上一个事件循环结束后,连续输出三段字符串
正常情况下,一个 Future 异步任务的执行是相对简单的:在我们声明一个 Future 时,Dart 会将异步任务的函数执行体放入事件队列,然后立即返回,后续的代码继续同步执行。而当同步执行的代码执行完毕后,事件队列会按照加入事件队列的顺序(即声明顺序),依次取出事件,最后同步执行 Future 的函数体及后续的 then。
then 与 Future 函数体共用一个事件循环
而如果 Future 有多个 then,它们也会按照链式调用的先后顺序同步执行,同样也会共用一个事件循环。
如果 Future 执行体已经执行完毕了,但你又拿着这个 Future 的引用,往里面加了一个 then 方法体,这时 Dart 会如何处理呢?面对这种情况,Dart 会将后续加入的 then 方法体放入微任务队列,尽快执行。
下面的代码演示了 Future 的执行规则,即,先加入事件队列,或者先声明的任务先执行;then 在 Future 结束后立即执行。
- 在第一个例子中,由于 f1 比 f2 先声明,因此会被先加入事件队列,所以 f1 比 f2 先执行;
- 在第二个例子中,由于 Future 函数体与 then 共用一个事件循环,因此 f3 执行后会立刻同步执行 then 3;
- 最后一个例子中,Future 函数体是 null,这意味着它不需要也没有事件循环,因此后续的 then 也无法与它共享。在这种场景下,Dart 会把后续的 then 放入微任务队列,在下一次事件循环中执行。
//f1 比 f2 先执行
Future(() => print('f1'));
Future(() => print('f2'));
//f3 执行后会立刻同步执行 then 3
Future(() => print('f3')).then((_) => print('then 3'));
//then 4 会加入微任务队列,尽快执行
Future(() => null).then((_) => print('then 4'));
再来一个例子
Future(() => print('f1'));// 声明一个匿名 Future
Future fx = Future(() => null);// 声明 Future fx,其执行体为 null
// 声明一个匿名 Future,并注册了两个 then。在第一个 then 回调里启动了一个微任务
Future(() => print('f2')).then((_) {
print('f3');
scheduleMicrotask(() => print('f4'));
}).then((_) => print('f5'));
// 声明了一个匿名 Future,并注册了两个 then。第一个 then 是一个 Future
Future(() => print('f6'))
.then((_) => Future(() => print('f7')))
.then((_) => print('f8'));
// 声明了一个匿名 Future
Future(() => print('f9'));
// 往执行体为 null 的 fx 注册了了一个 then
fx.then((_) => print('f10'));
// 启动一个微任务
scheduleMicrotask(() => print('f11'));
print('f12');
f12
f11
f1
f10
f2
f3
f5
f4
f6
f9
f7
f8
- 因为其他语句都是异步任务,所以先打印 f12。
- 剩下的异步任务中,微任务队列优先级最高,因此随后打印 f11;然后按照 Future 声明的先后顺序,打印 f1。
- 随后到了 fx,由于 fx 的执行体是 null,相当于执行完毕了,Dart 将 fx 的 then 放入微任务队列,由于微任务队列的优先级最高,因此 fx 的 then 还是会最先执行,打印 f10。
- 然后到了 fx 下面的 f2,打印 f2,然后执行 then,打印 f3。f4 是一个微任务,要到下一个事件循环才执行,因此后续的 then 继续同步执行,打印 f5。本次事件循环结束,下一个事件循环取出 f4 这个微任务,打印 f4。
- 然后到了 f2 下面的 f6,打印 f6,然后执行 then。这里需要注意的是,这个 then 是一个 Future 异步任务,因此这个 then,以及后续的 then 都被放入到事件队列中了。
- f6 下面还有 f9,打印 f9。
- 最后一个事件循环,打印 f7,以及后续的 f8。
then 会在 Future 函数体执行完毕后立刻执行,无论是共用同一个事件循环还是进入下一个微任务。
import 'dart:async';
void main() {
var future = new Future.value('a').then((v1) {
return new Future.value('$v1 b').then((v2) {
return new Future.value('$v2 c').then((v3) {
return new Future.value('$v3 d');
});
});
});
future.then(print, onError: print);
}
Future.catchError
import 'dart:async';
void main() {
new Future.error('boom!').catchError(print, test: (error) {
return error is String;
});
}
then中的回调onError和Future.catchError
Future.then中的onError回调只处理原始Future抛出的错误,不能处理回调函数抛出的错误,onError回到抛出的错误会使Future完成并返回错误
import 'dart:async';
void main() {
new Future.error('boom!').then(print, onError: (error) {
print('handle original error: $error');
throw 'new error';
}).then(print, onError: (error) {
print('handle new error: $error');
});
}
then可返回一个Future,但是onError只能处理当前Future的错误。
当多个Future链式调用的时候,并不需要关心错误来自哪里,可以用catchError来捕获错误,不建议同时使用onError和catchError
import 'dart:async';
void main() {
new Future.value(1).then((v) {
return new Future.error('boom!');
}).then((v) {
return new Future.value('hello');
}).catchError((error) {
print('error: $error');
});
}
Future.whenComplete
使用then().catchError() 这种模式类似于try-catch,
Future.whenComplete 类似于 finally
Future.whenComplete 在Future完成之后总是会调用,并且返回一个Future对象。
import 'dart:async';
import 'dart:math';
void main() {
var random = new Random();
new Future.delayed(new Duration(seconds: 3), () {
if (random.nextBool()) {
return 100;
} else {
throw 'boom!';
}
}).then(print).catchError(print).whenComplete(() {
print('done!');
});
}
Future.timeout
原始Future会在5s后完成,但是timeout声明的是2s后超时,所以2s后Future会抛出TimeoutException
import 'dart:async';
void main() {
new Future.delayed(new Duration(seconds: 5), () {
return 1;
}).timeout(new Duration(seconds: 2)).then(print).catchError(print);
}
创建一个Future
import 'dart:async';
void main() {
new Future(() {
var sum = 0;
for (var i = 0; i < 50000; i++) {
sum += i;
}
return sum;
}).then(print);
}
Future中的函数体会异步的执行,函数返回值将作为Future完成时候的值
Future.sync接受一个函数,并且立即运行,返回一个Future对象。该方法可将普通函数包装成Future。
Future.microtask是调用scheduleMicrotask异步执行函数体
async await
import 'dart:async';
void main() async {
var future = new Future.delayed(new Duration(seconds: 3), () {
return 1;
});
var result = await future;
print(result + 1);
}
错误处理
import 'dart:async';
void main() async {
var future = new Future.delayed(new Duration(seconds: 3), () {
return new Future.error('boom!');
});
try {
var result = await future;
print(result + 1);
} catch (e) {
print('error: $e');
} finally {
print('done!');
}
}
顺序调用Future
void main() async {
var fun1 = (int v) async => v + 1;
var fun2 = (int v) async => v - 1;
var fun3 = (int v) async => v * 10;
var value = 10;
value = await fun1(value);
value = await fun2(value);
value = await fun3(value);
print(value);
}
Future.foreach
根据某个集合,创建一系列的Future,并且会按顺序执行这些Future。 比如下面的例子,根据{1,2,3}创建3个延迟对应秒数的Future。执行结果为1秒后打印1,再过2秒打印2,再过3秒打印3,总时间为6秒。
Future.forEach({1,2,3}, (num){
return Future.delayed(Duration(seconds: num),(){print(num);});
});
Future.wait
用来等待多个future完成,并收集它们的结果。那这样的结果就有两种情况了:
- 如果所有future都有正常结果返回:则future的返回结果是所有指定future的结果的集合
- 如果其中一个future有error返回:则future的返回结果是第一个error的值
var future1 = new Future.delayed(new Duration(seconds: 1), () => 1);
var future2 =
new Future.delayed(new Duration(seconds: 2), () => 2);
var future3 = new Future.delayed(new Duration(seconds: 3), () => 3);
Future.wait({future1,future2,future3}).then(print).catchError(print);
//运行结果: [1, 2, 3]
Future.any
返回的是第一个执行完成的future的结果,不会管这个结果是正确的还是error
Future
.any([1, 2, 5].map(
(delay) => new Future.delayed(new Duration(seconds: delay), () => delay)))
.then(print)
.catchError(print);
Future.doWhile()
重复性地执行某一个动作,直到返回false或者Future,退出循环。
Future.microtask
创建一个在microtask队列运行的future。
Dart官网的关于Future的说明
- 你通过then串起来的那些回调函数在Future完成的时候会被立即执 行,也就是说它们是同步执行,而不是被调度异步执行。
- 如果Future在调用then串起回调函数之前已经完成,
那么这些回调函数会被调度到微任务队列异步执行。
- 通过Future()和Future.delayed()实例化的Future不会同步执行,它们会被调度到事件队列异步执行。
- 通过Future.value()实例化的Future会被调度到微任务队列异步完成,类似于第2条。
- 通过Future.sync()实例化的Future会同步执行其入参函数,然后(除非这个入参函数返回一个Future)调度到微任务队列来完成自己,类似于第2条。
Dart中的事件循环
event队列包含所有的外来事件:I/O,mouse events,drawing events,timers,isolate之间的message等。
在 Dart 中,实际上有两个队列,一个事件队列(Event Queue),另一个则是微任务队列(Microtask Queue)。在每一次事件循环中,Dart 总是先去第一个微任务队列中查询是否有可执行的任务,如果没有,才会处理后续的事件队列的流程。
微任务顾名思义,表示一个短时间内就会完成的异步任务。从上面的流程图可以看到,微任务队列在事件循环中的优先级是最高的,只要队列中还有任务,就可以一直霸占着事件循环。
异步任务我们用的最多的还是优先级更低的 Event Queue。比如,I/O、绘制、定时器这些异步事件,都是通过事件队列驱动主线程执行的。
Dart 为 Event Queue 的任务建立提供了一层封装,叫作 Future
正常情况下,一个 Future 异步任务的执行是相对简单的:在我们声明一个 Future 时,Dart 会将异步任务的函数执行体放入事件队列,然后立即返回,后续的代码继续同步执行。而当同步执行的代码执行完毕后,事件队列会按照加入事件队列的顺序(即声明顺序),依次取出事件,最后同步执行 Future 的函数体及后续的 then。
then 与 Future 函数体共用一个事件循环
而如果 Future 有多个 then,它们也会按照链式调用的先后顺序同步执行,同样也会共用一个事件循环。
如果 Future 执行体已经执行完毕了,但你又拿着这个 Future 的引用,往里面加了一个 then 方法体,这时 Dart 会如何处理呢?面对这种情况,Dart 会将后续加入的 then 方法体放入微任务队列,尽快执行。
下面的代码演示了 Future 的执行规则,即,先加入事件队列,或者先声明的任务先执行;then 在 Future 结束后立即执行。
再来一个例子
then 会在 Future 函数体执行完毕后立刻执行,无论是共用同一个事件循环还是进入下一个微任务。
Future.catchError
then中的回调onError和Future.catchError
Future.then中的onError回调只处理原始Future抛出的错误,不能处理回调函数抛出的错误,onError回到抛出的错误会使Future完成并返回错误
then可返回一个Future,但是onError只能处理当前Future的错误。
当多个Future链式调用的时候,并不需要关心错误来自哪里,可以用catchError来捕获错误,不建议同时使用onError和catchError
Future.whenComplete
使用
then().catchError()这种模式类似于try-catch,Future.whenComplete类似于finallyFuture.whenComplete 在Future完成之后总是会调用,并且返回一个Future对象。
Future.timeout
原始Future会在5s后完成,但是timeout声明的是2s后超时,所以2s后Future会抛出
TimeoutException创建一个Future
Future中的函数体会异步的执行,函数返回值将作为Future完成时候的值
Future.sync接受一个函数,并且立即运行,返回一个Future对象。该方法可将普通函数包装成Future。
Future.microtask是调用scheduleMicrotask异步执行函数体
async await
错误处理
顺序调用Future
Future.foreach
根据某个集合,创建一系列的Future,并且会按顺序执行这些Future。 比如下面的例子,根据{1,2,3}创建3个延迟对应秒数的Future。执行结果为1秒后打印1,再过2秒打印2,再过3秒打印3,总时间为6秒。
Future.wait
用来等待多个future完成,并收集它们的结果。那这样的结果就有两种情况了:
Future.any
返回的是第一个执行完成的future的结果,不会管这个结果是正确的还是error
Future.doWhile()
重复性地执行某一个动作,直到返回false或者Future,退出循环。
Future.microtask
创建一个在microtask队列运行的future。
Dart官网的关于Future的说明
那么这些回调函数会被调度到微任务队列异步执行。