如何使异步Dart调用同步?

Tin*_*ndt 9 asynchronous synchronous dart

我正在通过将各种Java程序移植到Dart并对结果进行比较和分析来评估Dart对德国公司的影响.在浏览器中,Dart赢得了胜利.对于服务器软件来说,性能似乎是一个严重的问题(请参阅我的这个问题),但这大部分都被解除了.

现在我正在移植一些"简单"的命令行工具,我根本没想到任何严重的问题,但至少有一个.有些工具会发出HTTP请求来收集一些数据,而独立的Dart虚拟机只能以异步方式支持它们.仔细研究一下,我发现似乎不可能在大多数同步软件中使用任何异步调用.

我知道我可以将可用的同步软件重组为异步软件.但这会将设计良好的软件转换为可读性较低且难以调试和维护的软件.对于某些软件而言,这是没有意义的.我的问题:是否存在(被我忽视)将异步调用嵌入到同步调用方法中的方法?

我想,提供一个系统调用并不困难,只能从主线程中使用,它只是将执行转移到整个排队的异步函数调用列表(不必先结束主线程)并尽快当最后一个执行返回并继续主线程.

可能看起来像这样的东西:

var synchFunction() {
  var result;
  asyncFunction().then(() { result = ...; });

  resync(); // the system call to move to and wait out all async execution

  return result;
}
Run Code Online (Sandbox Code Playgroud)

使用这样的方法也可以简化lib API.大多数"同步"调用都可以被删除,因为重新同步调用可以完成这项工作.这似乎是一个合乎逻辑的想法,我仍然认为它存在,我错过了它.或者有一个严重的原因,为什么这不起作用?


在考虑了lm(见下文)收到的答案两天之后,我仍然不明白为什么不应该将异步Dart调用封装到同步调用中.它始终在"正常"同步编程世界中完成.通常,您可以通过从异步例程获取"完成"或者在超时后继续失败来等待重新同步.

考虑到这一点,我的第一个提案可以像这样增强:

var synchFunction() {
  var result;
  asyncFunction()
    .then(() { result = ...; })
    .whenComplete(() { continueResync() }); // the "Done" message

  resync(timeout); // waiting with a timeout as maximum limit

  // Either we arrive here with the [result] filled in or a with a [TimeoutException].
  return result;
}
Run Code Online (Sandbox Code Playgroud)

resync()确实会结束后通常会发生相同main的分离方法,它开始执行排队异步函数(或等待事件,使它们可执行文件).一旦遇到continueResync()调用,就会设置一个标志,该标志会停止此异步执行并resync()返回主线程.如果没有continueResync()在给定的遇到通话timeout期间它也中止异步执行,并离开resync()TimeoutException.

对于某些受益于直接同步编程(不是客户端软件而不是服务器软件)的软件组,这样的功能可以为必须处理异步库的程序员解决许多问题.

我相信我也在lm下面的论证中找到了主要论点的解决方案.因此,我的问题仍然存在于我提出的这种"增强型"解决方案中:在Dart中是否有任何真正无法实现的解决方案

bik*_*868 8

只有在不需要获取返回值时,才能将异步方法包装在同步方法中.

例如,如果要禁用保存按钮,请将结果异步保存到服务器,并在作业完成后重新启用保存按钮,您可以这样写:

Future<bool> save() async {
  // save changes async here
  return true;
}

void saveClicked() {
  saveButton.enabled = false;
  save()
    .then((success) => window.alert(success ? 'Saved' : 'Failed'))
    .catchError((e) => window.alert(e))
    .whenComplete(() { saveButton.enabled = true; });
}
Run Code Online (Sandbox Code Playgroud)

请注意,该saveClicked方法是完全同步的,但是save异步执行该方法.

请注意,如果进行saveClicked异步,则不仅需要使用异步模式调用它,而且整个方法体将以异步方式运行,因此在函数返回时不会禁用保存按钮.

为了完整性,异步版本saveClicked看起来像这样:

Future<Null> saveClicked() async {
  saveButton.enabled = false;
  try {
    bool success = await save();
    window.alert(success ? 'Saved' : 'Failed');
  }
  catch (e) {
    window.alert(e);
  }
  finally {
    saveButton.enabled = true;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为“将异步方法变成同步方法”是具有误导性的。从同步调用调度异步调用,然后将其结果忽略。无论如何,同步调用始终保持同步。这只是在异步调用完成时是否传递回调以在特定点继续执行。 (3认同)
  • 我同意,也许我可以改写我的答案。由于问题是关于在同步应用程序中使用异步函数库,因此我想指出,如果您不关心返回值,则可以将异步函数包装在同步函数中,如图所示。这可能对找到此线程的人有帮助。 (2认同)

Lev*_*hes 8

是的,这已经很晚了,但我认为这是新人应该知道的一个很酷的功能。

这里一个办法,但飞镖文档警惕它(和它的某种方式“实验”,虽然影响并没有真正讨论)。

waitFor命令。

您基本上传入一个异步函数,该函数返回 a Future,一个可选timeout参数,该waitFor函数将返回结果。

例如:

final int number = waitFor<int>(someAsyncThatReturnsInt);
Run Code Online (Sandbox Code Playgroud)

  • 看起来你不能在 flutter 中使用它,它仅适用于 cli (12认同)

lrn*_*lrn 6

resync函数无法在 Dart 当前的执行模型中实现。

异步执行具有传染性。同步函数必须在任何其他异步事件可以执行之前返回,因此无法同步等待异步执行。

Dart 中的执行是单线程和基于事件的。如果resync函数不阻塞同一隔离中的所有其他执行,则无法阻塞,因此挂起的异步操作永远不会发生。

要阻止同步执行并继续执行其他操作,您需要保留到该点的整个调用堆栈,并在同步操作完成后恢复它。如果你有那个功能,那么可能有比 Future 和 Stream 更好的方法来做事:)

此外,等待“所有异步执行”在基于事件的系统中没有明确定义。可能有来自网络的广播流发射事件、定期计时器或从另一个隔离区获取数据的接收端口,或者您无法等待的其他一些事件源,因为它们来自隔离区之外,或者事件过程。当当前隔离关闭时,它可能会向另一个隔离发送最终关闭消息,因此有效地“异步执行”直到隔离死亡才结束。

使用 async/await 语法,你不会得到同步操作,但是编写类似的异步操作会更容易:

function() async {
  var result = await asyncFunction();
  return result;
}
Run Code Online (Sandbox Code Playgroud)

它不会等待未反映在Future返回的异步操作中asyncFunction,但这是在asyncFunction其操作完成之前不完成的工作。

  • async/await 的问题在于,它看起来不错,似乎很容易使用,但它已经无法调试软件了。特别是对于本质上同步并且仅使用一两次异步函数调用的软件来说,仅仅为了处理这种情况而使几乎每个函数都异步会浪费代码和资源。当我被迫这样做时,我的生产力降到了零,主要是因为调试不再可用。 (2认同)