《The Event Loop and Dart》译文
原文:https://webdev.dartlang.org/articles/performance/event-loop
Event loops and queues
event loop循环的从event queue中取出一个event并执行。
event queue中的event可以是用户输入、文件IO、定时器等等:
Dart’s single thread of execution
一旦一个dart function开始执行,就会继续执行下去直到它运行结束,换句话说,一条dart语句不能被其他的dart语句打断。
注意: A Dart command-line app 可以在不同的isolate上平行的执行代码。(Dart web app 不能直接创建额外的isolate,但可以创建worker) isolate相互隔离、不共享内存、通过传递消息来完成之间的通信。 除显示调用方法将dart语句放在isolate或者worker以外,dart语句全部运行在主线程。更多的信息可以参考 Use isolates or workers if necessary 。
Dart APP当在主isolate中调用完main()方法后,就按顺序循环执行event queue中的event,如下图:
Dart’s event loop and queues
Dart APP存在一个单独的event loop和两个event queue(event queue 和 microtask queue)。
Microtask Queue存在的意义是在处理一个event时,部分语句稍后执行,但希望在下一个event之前处理完毕。
例如,当一个状态发生改变时,它将若干变化组合一起同步报告。microtask queue使得在DOM在显示状态变化前,状态可发生多次变化。
event queue包含了dart自定义event、系统event,但microtask queue只包含了自定义event,但我们期望Web实现包含浏览器microtask queue。
注意:当event looper正在处理microtask queue中的Event时候,event queue中的event就停止了处理了,此时App不能绘制任何图形,不能处理任何鼠标点击,不能处理文件IO等等
Event-Looper优先执行Microtask Queue中的Event,直到Microtask Queue为空时,才会执行Event Queue中的Event
Dart中只能知道Event处理的先后顺序,但是并不知道某个Event执行的具体时间点,因为它的处理模型是一个单线程循环,而不是基于时钟调度(即它的执行只是按照Event处理完,就开始循环下一个Event,而与Java中的Thread调度不一样,没有时间调度的概念),也就是我们既是指定另一个Delay Time的Task,希望它在预期的时间后开始执行,它有可能不会在那个时间执行,需要看是否前面的Event是否已经Dequeue。
链式调用明确语句顺序
如果语句之间存在依赖关系,那就明确依赖关系。明确的依赖关系让别人能更容易理解。
下面是一种错误的用法:
// BAD because of no explicit dependency between setting and using
// the variable.
future.then(...set an important variable...);
Timer.run(() {...use the important variable...});
应该替换为:
// BETTER because the dependency is explicit.
future.then(...set an important variable...)
.then((_) {...use the important variable...});
好的做法是用then()去明确变量设置和使用的前后顺序。(如果不管依赖是否正确执行,就要执行后续的语句,可以使用whenComplete()去代替when())
如果变量设置是异步操作,建议将变量使用语句放在new Future中。
// MAYBE EVEN BETTER: Explicit dependency plus delayed execution.
future.then(...set an important variable...)
.then((_) {new Future(() {...use the important variable...})});
How to schedule a task
当有代码需要在后续任务执行的时候,可以通过dart:async提供的两种方法实现:
1.使用Future类,可以将任务加入到Event Queue的队尾
2.使用scheduleMicrotask函数,将任务加入到Microtask Queue队尾
Use the appropriate queue (usually: the event queue)
尽可能使用event queue,microtask queue会阻塞event queue的执行。
如果语句需要在event queue中其他event之前执行,可以通过scheduleMicrotask()添加到microtask queue。
Event queue: new Future()
可以通过new Future() 或者 new Future.delayed()来使用event queue。
注意:也可以使用Timer来调用event queue, 但是如果存在未截获的exception,app会退出。
立即加入event queue,使用new Future():
// Adds a task to the event queue.
new Future(() {
// ...code goes here...
});
等待一段时间后,再加入event queue,使用use new Future.delayed():
// After a one-second delay, adds a task to the event queue.
new Future.delayed(const Duration(seconds:1), () {
// ...code goes here...
});
当需要做动画的时候,不要使用Future,而需要使用animateFrame
PS:
1.FutureTask执行完成后,then()会立即执行(then并没有创建新的event丢到event 1ueue中,而只是Function Call);
2.当FutureTask在then()被调用之前执行完成,则会创建一个task,并将该task的添加到microtask queue中;
3.Future只是创建了一个event,将event插入到了event queue的队尾;
4.Future.value()跟第二条一样,创建Task丢到microtask Queue中;
5.Future.sync()立即执行传入的function,如果function返回future,则会创建Task丢到microtask Queue中执行。
Microtask queue: scheduleMicrotask()
使用scheduleMicrotask
在dart:async库中定义了scheduleMicrotask()方法来使用microtask queue。
scheduleMicrotask(() {
// ...code goes here...
});
另外一种使用microtask queue的方式,是在Future中调用then()。
Use isolates or workers if necessary
当有计算很繁重的任务时,则需要使用isolate或者Worker来执行,以保持App对用户操作的及时响应。Isolate可能是一个单独的线程,或者一个单独的进程,这取决于Dart的具体实现。通常情况下可以根据你的cpu的个数来决定isolate的数量。但你也可以使用超过cpu个数的isolate,前提是你有好的app架构,让不同的isolate来分担不同的代码块运行,并能保证这些isolate之间没有数据共享。