不同操作语言处理耗时任务有不同的处理机制:
事件循环并不复杂,Dart维护着一个事件队列(Event Queue),并对事件进行存取操作:
在Dart单线程模型中,代码执行顺序如下:
事件循环(Event Loop)
,并开始执行队列中的任务;FIFO
(先进先出)的顺序,执行 微任务队列(Microtask Queue)
中的所有任务;FIFO
(先进先出)的顺序,执行 事件队列(Event Queue)
中的所有任务;注意:当事件循环正在处理微任务队列的时候。事件队列会被堵塞。这时候APP就无法进行UI绘制,响应鼠标事件和I/O等事件。
虽然可以预测任务执行的顺序,但是我们无法预测事件循环什么时候会从队列中提取任务。Dart事件处理系统基于单线程循环,而不是基于时基(tick,系统的相对时间单位)或者其他的时间度量。例如,当你创建一个延时1s的任务,1s后向事件队列添加一个任务,但在该任务之前的任务结束前,事件循环是不会处理这个任务的,也就是说该任务执行可能是大于1s的。
我们通过Future链指定任务执行顺序:
future.then(...set an important variable...)
.then((_) {...use the important variable...});
也可以使用Future.whenComplete()
来指定任务在最后被调用。
可以使用new Future()
或new Future.delayed()
向事件队列种添加事件,这是dart:async
中定义的两个Future的构造函数。
值得注意的是,使用new Future.delayed()
在延时一定时间后向队列插入一个任务,这个任务想要执行必须满足下面几点:
dart:async
中定义了scheduleMicrotask()
为我们提供创建微任务队列的方法:
scheduleMicrotask(() {
// ...code goes here...
});
创建一个简单的用于打印的微任务:
import "dart:async";
main(List<String> args) {
scheduleMicrotask(() {
print("I am microtask");
});
}
关于执行顺序,我做了下面几点总结:
FIFO(先进先出)
原则,先入队先执行Future.delay()
延时任务执行,是在执行该语句延时一定时长后在将Future任务
插入队列尾,在测试中一般任务是最后执行Future
如果执行完再添加then
,该任务会被放入微任务队列中,当前Future
执行完后会立刻执行该微任务,执行完该微任务后才执行下一个Future
,另外,该微任务如果新建一个Future
并没返回,该Future
将插入事件队列(除延时任务)的队尾Future
的 then
未执行完,下一个then
不会执行。练习一:
import "dart:async";
main(List<String> args) {
new Future(() => print('future'));
scheduleMicrotask(() => print('microtask'));
print('main');
}
运行结果:
main
microtask
future
Dart中事件的执行顺序:main方法 > 微任务队列 > 事件队列,对应总结中的1,2点。
练习二:
import 'dart:async';
main() {
print('main #1 of 2');
scheduleMicrotask(() => print('microtask #1 of 2'));
new Future.delayed(new Duration(seconds:1),
() => print('future #1 (delayed)'));
new Future(() => print('future #2 of 3'));
new Future(() => print('future #3 of 3'));
scheduleMicrotask(() => print('microtask #2 of 2'));
print('main #2 of 2');
}
运行结果:
main #1 of 2
main #2 of 2
microtask #1 of 2
microtask #2 of 2
future #2 of 3
future #3 of 3
future #1 (delayed)
该练习是练习一的加强版,同时对应总结第3点,Future.delay()
将事件放入事件队列队尾。
练习三:
import 'dart:async';
main() {
print('main #1 of 2');
scheduleMicrotask(() => print('microtask #1 of 3'));
new Future.delayed(new Duration(seconds:1),
() => print('future #1 (delayed)'));
new Future(() => print('future #2 of 4'))
.then((_) => print('future #2a'))
.then((_) {
print('future #2b');
scheduleMicrotask(() => print('microtask #0 (from future #2b)'));
})
.then((_) => print('future #2c'));
scheduleMicrotask(() => print('microtask #2 of 3'));
new Future(() => print('future #3 of 4'))
.then((_) => new Future(
() => print('future #3a (a new future)')))
.then((_) => print('future #3b'));
new Future(() => print('future #4 of 4'));
scheduleMicrotask(() => print('microtask #3 of 3'));
print('main #2 of 2');
}
运行结果:
main #1 of 2
main #2 of 2
microtask #1 of 3
microtask #2 of 3
microtask #3 of 3
future #2 of 4
future #2a
future #2b
future #2c
microtask #0 (from future #2b)
future #3 of 4
future #4 of 4
future #3a (a new future)
future #3b
future #1 (delayed)
首先,根据规则1~3,main()函数先执行,然后微任务执行,然后有延时的事件任务是最后:
main #1 of 2
main #2 of 2
microtask #1 of 3
microtask #2 of 3
microtask #3 of 3
.
.
.
future #1 (delayed)
接着事件队列开始执行,根据总结5,链式调用:
future #2 of 4
future #2a
future #2b
future #2c
而在future #2b
中新建微任务future #3a (a new future)
,会在当前Future
执行完后执行
接着开始执行future #3
, 在其then函数中,新建future #3a
并插入队尾,由于future #3a
没执行,所以future #3b也不会马上执行。
这时,队尾情况变为
future #3a (a new future)
future #3b
future #1 (delayed)
之后执行future #4 of 4
需要注意的是,如果把future #3
中then
的代码
由.then((_) => new Future( () => print(‘future #3a (a new future)’)))
改为.then((_){ new Future( () => print(‘future #3a (a new future)’));})
因为没有return
语句,这时候回调函数返回的是Null Future,future #3a
添加到事件队列,但不会阻塞future #3b
执行。
执行的结果如下,大家可以体会下其中差别:
main #1 of 2
main #2 of 2
microtask #1 of 3
microtask #2 of 3
microtask #3 of 3
future #2 of 4
future #2a
future #2b
future #2c
microtask #0 (from future #2b)
future #3 of 4
future #3b
future #4 of 4
future #5 of 5
future #3a (a new future)
future #1 (delayed)
练习四
import 'dart:async';
main() {
print('main #1 of 2');
Future future1 = new Future(() => print('future #1 of 5'));
Future future2 = new Future(() => null);
Future future3 = new Future.delayed(Duration(seconds: 1) ,() => print('future #3 of 5'));
Future future4 = new Future(() => null);
Future future5 = new Future(() => null);
future5.then((_) => print('future #5a'));
future4.then((_) {
print('future #4a');
new Future(() => print('future #4b (a new future)'));
future2.then((_) {
print('future #2a from future4 #4a');
});
});
future2.then((m) {
print('future #2b');
});
print('main #2 of 2');
}
运行结果:
main #1 of 2
main #2 of 2
future #1 of 5
future #2b
future #4a
future #2a from future4 #4a
future #5a
future #4b (a new future)
future #3 of 5
练习三基本包括了练习四的要点,这里就不详细解释 (主要因为懒 ),主要注意的一点是,future4先于feture5定义,future4的then也会先于future5执行。
为了使应用程序保持响应,应该将任务放入Isolate中。Isolate可能运行在一个单独的进程或线程中,这取决于Dart的具体实现。我们已经知道Dart是单线程的,这个线程有自己可以访问的内存空间以及需要运行的事件循环,我们可以将这个空间系统称之为是一个Isolate,比如Flutter中就有一个Root Isolate,负责运行Flutter的代码,比如UI渲染、用户交互等等。
那么应该使用多少Isolate隔离区?
对于计算密集型任务,一般隔离区的数量取决于你CPU有多少可用。
如何创建隔离区?
创建Isolate是比较简单的,我们通过Isolate.spawn
就可以创建了:
import "dart:isolate";
main(List<String> args) {
Isolate.spawn(foo, "I am new Isolate");
}
void foo(info) {
print("From new isolate:$info");
}
参考:
http://www.cndartlang.com/890.html
https://juejin.im/post/5d36bd3ff265da1b9570995b
https://segmentfault.com/a/1190000020398241