1

我想启动一个相当昂贵的操作来响应用户点击画布元素。

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”从上一次单击运行完成之前不会发生任何事情。

如何使用期货在新线程上简单地启动计算密集型功能,以便我可以将其中的多个排队以响应多次点击,即使第一个未来尚未完成?

谢谢。

4

2 回答 2

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);
}

(我还没有测试过以上,但类似的东西应该可以工作。)

于 2013-04-20T04:53:24.230 回答
1

事件循环和事件队列

您应该注意 Futures 不是线程。它们不会同时运行,事实上,Dart 是单线程的。所有 Dart 代码都在事件循环中运行。

事件循环是一个循环,只要当前的 Dart 隔离还活着,它就会运行。当你调用main()启动一个 Dart 应用程序时,isolate 被创建,并且在 main 方法完成并且事件队列上的所有项目也完成后它不再活动。

事件队列是仍然需要完成执行的所有函数的集合。因为 Dart 是单线程的,所以所有这些函数都需要一次运行一个。因此,当事件队列中的一项完成时,另一项开始。事件队列的确切时间和调度比我自己解释的要复杂得多。

因此,异步处理对于防止单个线程被一些长时间运行的执行阻塞很重要。在 UI 中,较长的过程可能会导致视觉混乱并阻碍您的应用程序。

期货

Futures 表示将在 Future 的某个时间可用的值,因此得名。创建 Future 后,它会立即返回,并继续执行。

与该 Future 关联的回调(在您的情况下为expensiveFunction)是通过添加到事件队列来“启动”的。当您从当前隔离返回时,回调会运行,并且会尽快运行then.

因为您的 Future 定义为异步的,并且您不知道它们何时返回,所以您希望将回调排队,以便它们保持有序。

Stream是一个对象,它发出可以订阅的事件。当你写作时,canvasElement.onClick.listen(...)你要求的是onClickStream 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的文章。

于 2013-04-20T07:37:50.650 回答