事件循环和事件队列
您应该注意 Futures 不是线程。它们不会同时运行,事实上,Dart 是单线程的。所有 Dart 代码都在事件循环中运行。
事件循环是一个循环,只要当前的 Dart 隔离还活着,它就会运行。当你调用main()
启动一个 Dart 应用程序时,isolate 被创建,并且在 main 方法完成并且事件队列上的所有项目也完成后它不再活动。
事件队列是仍然需要完成执行的所有函数的集合。因为 Dart 是单线程的,所以所有这些函数都需要一次运行一个。因此,当事件队列中的一项完成时,另一项开始。事件队列的确切时间和调度比我自己解释的要复杂得多。
因此,异步处理对于防止单个线程被一些长时间运行的执行阻塞很重要。在 UI 中,较长的过程可能会导致视觉混乱并阻碍您的应用程序。
期货
Futures 表示将在 Future 的某个时间可用的值,因此得名。创建 Future 后,它会立即返回,并继续执行。
与该 Future 关联的回调(在您的情况下为expensiveFunction
)是通过添加到事件队列来“启动”的。当您从当前隔离返回时,回调会运行,并且会尽快运行then
.
流
因为您的 Future 定义为异步的,并且您不知道它们何时返回,所以您希望将回调排队,以便它们保持有序。
Stream是一个对象,它发出可以订阅的事件。当你写作时,canvasElement.onClick.listen(...)
你要求的是onClick
Stream MouseEvents
,然后你订阅它listen
。
您可以使用 Streams 将事件排队并在这些事件上注册回调以运行您想要的代码。
写什么
main() {
// Used to add events to a stream.
var controller = new StreamController<Future>();
// Pause when we get an event so that we take one value at a time.
var subscription = controller.stream.listen(
(_) => subscription.pause());
var canvas = new CanvasElement();
canvas.onClick.listen((MouseEvent e) {
print("entering event handler");
var future = new Future<int>(expensiveFunction);
// Resume subscription after our callback is called.
controller.add(future.then(redrawCanvas).then(subscription.resume()));
print("done event handler");
});
}
expensiveFunction() {
for(int i = 0; i < 1000000000; i++){
//do something insane here
}
}
redrawCanvas(int value) {
//do stuff here
print("redrawing canvas");
}
在这里,我们通过在每次鼠标单击后暂停来排队redrawCanvas
回调,然后在redrawCanvas
调用后恢复。
更多信息
另请参阅this great answer to a similar question。
开始阅读 Dart 的异步的好地方是这篇关于 dart:io 库的文章的第一部分和这篇关于 dart:async 库的文章。
有关期货的更多信息,请参阅这篇关于期货的文章。
有关 Streams 的信息,请参阅有关添加到 Streams的文章和有关创建 Streams的文章。