我想在用户点击画布元素时启动相当昂贵的操作。
mouseDown(MouseEvent e) {
print("entering event handler");
var future = new Future<int>(expensiveFunction);
future.then((int value) => redrawCanvas(value);
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");
}
我对M4 Dart的理解是,这个未来的构造函数应该异步启动“expensiveFunction”,也就是在与主要的不同的线程上。它确实以这种方式显示,因为“完成事件处理程序”会立即打印到IDE的输出窗口中,然后一段时间后会打印“重绘画布”。但是,如果我再次单击该元素,则直到我的“expensiveFunction”从上一次单击运行完成后才会发生任何事情。
如何使用期货简单地在新线程上启动计算密集型功能,以便我可以将其中多个排队等候以响应多次点击,即使第一个未来尚未完成呢?
感谢。
答案 0 :(得分:3)
正如另一个答案中所提到的,期货只是“未来可用价值的占位符”。它们并不一定意味着并发。
Dart有一个并发隔离的概念。您可以生成隔离以在并行线程或进程中运行某些代码。
dart2js可以将隔离区编译为Web Workers。 Web Worker可以在单独的线程中运行。
尝试这样的事情:
import 'dart:isolate';
expensiveOperation(SendPort replyTo) {
var result = doExpensiveThing(msg);
replyTo.send(result);
}
main() async {
var receive = new ReceivePort();
var isolate = await Isolate.spawn(expensiveOperation, receive.sendPort);
var result = await receive.first;
print(result);
}
(我没有测试过上面的内容,但它应该有用。)
答案 1 :(得分:1)
你应该注意到Futures不是线程。它们不会同时运行,事实上,Dart是单线程的。所有Dart代码都在事件循环中运行。
事件循环是一个循环,只要当前的Dart隔离区处于活动状态就会运行。当您调用main()
启动Dart应用程序时,将创建隔离,并且在main方法完成并且事件队列中的所有项目也完成后它将不再存在。
事件队列是仍需要完成执行的所有功能的集合。因为Dart是单线程的,所有这些函数需要一次运行一个。因此,当事件队列中的一个项目完成时,另一个项目开始。事件队列的确切时间和日程安排比我自己解释的要复杂得多。
因此,异步处理对于防止单个线程被某些长时间运行的执行阻塞非常重要。在用户界面中,漫长的过程可能会导致视觉jankiness并阻碍您的应用。
期货代表将来某个时候可用的价值,因此得名。创建Future时,会立即返回,并继续执行。
与Future相关联的回调(在您的情况下,expensiveFunction
)通过添加到事件队列而“启动”。当您从当前隔离返回时,回调会尽快运行then
之后的代码。
因为您的Futures根据定义是异步的,并且您不知道它们什么时候返回,您想要排队回调以使它们保持有序。
Stream 是一个发出可以订阅的事件的对象。当您撰写canvasElement.onClick.listen(...)
时,您要求onClick
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类似的问题。
开始阅读有关Dart异步的好地方是this article about the dart:io library和this article about the dart:async library的第一部分。
有关期货的更多信息,请参阅this article about Futures。
有关Streams的信息,请参阅this article about adding to Streams和this article about creating Streams。