飞镖,如何在自己的功能中创造未来?

Dan*_*son 29 asynchronous future dart

是否有可能在Dart中创建自己的未来以从您的方法返回,或者您是否必须始终从其中一个dart异步库方法返回内置的未来返回?

我想定义一个函数,它总是返回它Future<List<Base>>是否实际执行异步调用(文件读取/ ajax/etc)或只是获取局部变量,如下所示:

List<Base> aListOfItems = ...;

Future<List<Base>> GetItemList(){

    return new Future(aListOfItems);

}
Run Code Online (Sandbox Code Playgroud)

Fox*_*x32 55

如果你需要创造未来,你可以使用Completer.请参阅文档中的Completer.这是一个例子:

Future<List<Base>> GetItemList(){
  var completer = new Completer();

  // At some time you need to complete the future:
  completer.complete(new List<Base>());

  return completer.future;
}
Run Code Online (Sandbox Code Playgroud)

但大多数时候你不需要用完成者来创造未来.就像在这种情况下:

Future<List<Base>> GetItemList(){
  var completer = new Completer();

  aFuture.then((a) {
    // At some time you need to complete the future:
    completer.complete(a);
  });

  return completer.future;
}
Run Code Online (Sandbox Code Playgroud)

使用完成符可能会使代码变得非常复杂.您可以简单地使用以下代码,因为也then()返回a Future:

Future<List<Base>> GetItemList(){
  return aFuture.then((a) {
    // Do something..
  });
}
Run Code Online (Sandbox Code Playgroud)

或者文件io的示例:

Future<List<String>> readCommaSeperatedList(file){
  return file.readAsString().then((text) => text.split(','));
}
Run Code Online (Sandbox Code Playgroud)

有关更多提示,请参阅此博客文章.


Sur*_*gch 29

从你自己的函数返回一个未来

这个答案是您可以做到的许多方法的总结。

初始点

您的方法可以是任何东西,但为了这些示例,假设您的方法如下:

int cubed(int a) {
  return a * a * a;
}
Run Code Online (Sandbox Code Playgroud)

目前你可以像这样使用你的方法:

int myCubedInt = cubed(3);  // 27
Run Code Online (Sandbox Code Playgroud)

但是,您希望您的方法返回Future这样的:

Future<int> myFutureCubedInt = cubed(3);
Run Code Online (Sandbox Code Playgroud)

或者能够像这样更实际地使用它:

int myCubedInt = await cubed(3);
Run Code Online (Sandbox Code Playgroud)

以下解决方案都显示了这样做的方法。

解决方案 1:Future() 构造函数

最基本的解决方案是使用Future.

Future<int> cubed(int a) {
  return Future(() => a * a * a);
}
Run Code Online (Sandbox Code Playgroud)

我将方法的返回类型更改为Future<int>,然后将旧函数的工作作为匿名函数传递给Future构造函数。

解决方案 2:Future 命名构造函数

期货可以通过一个值或一个错误来完成。因此,如果您想明确指定这些选项中的任何一个,您可以使用Future.valueFuture.error命名构造函数。

Future<int> cubed(int a) {
  if (a < 0) {
    return Future.error(ArgumentError("'a' must be positive."));
  }
  return Future.value(a * a * a);
}
Run Code Online (Sandbox Code Playgroud)

不允许为负值a是一个人为的例子来展示Future.error构造函数的使用。如果没有什么会产生错误,那么您可以简单地使用Future.value构造函数,如下所示:

Future<int> cubed(int a) {
  return Future.value(a * a * a);
}
Run Code Online (Sandbox Code Playgroud)

解决方案3:异步方法

一个async方法会自动返回 a,Future因此您可以标记该方法async并更改返回类型,如下所示:

Future<int> cubed(int a) async {
  return a * a * a;
}
Run Code Online (Sandbox Code Playgroud)

通常您async与 结合使用await,但没有任何内容表明您必须这样做。Dart 自动将返回值转换为Future.

如果您正在使用另一个Future在函数体内返回 a 的 API ,您可以await像这样使用:

Future<int> cubed(int a) async {
  return await cubedOnRemoteServer(a);
}
Run Code Online (Sandbox Code Playgroud)

或者这是使用Future.then语法相同的事情:

Future<int> cubed(int a) async {
  return cubedOnRemoteServer(a).then((result) => result);
}
Run Code Online (Sandbox Code Playgroud)

解决方案 4:完成者

使用 aCompleter是最低级别的解决方案。仅当您有一些上述解决方案未涵盖的复杂逻辑时,才需要执行此操作。

import 'dart:async';

Future<int> cubed(int a) async {
  final completer = Completer();
  if (a < 0) {
    completer.completeError(ArgumentError("'a' must be positive."));
  } else {
    completer.complete(a * a * a);
  }
  return completer.future;
}
Run Code Online (Sandbox Code Playgroud)

此示例类似于上面的命名构造函数解决方案。除了以正常方式完成未来之外,它还处理错误。

关于阻止 UI 的说明

使用 future 并不能保证您不会阻塞 UI(即主隔离)。从你的函数返回一个 future 只是告诉 Dart 在事件队列的末尾安排任务。如果该任务很密集,当事件循环安排它运行时,它仍然会阻塞 UI。

如果您有一个密集型任务想要在另一个隔离上运行,那么您必须生成一个新的隔离来运行它。当任务在另一个隔离上完成时,它将返回一条消息作为未来,您可以将其作为函数的结果传递。

许多标准的 Dart IO 类(如FileHttpClient)都有将工作委托给系统的方法,因此不会在您的 UI 线程上进行密集的工作。因此,这些方法返回的期货不会阻止您的 UI。

也可以看看

  • 这是一个很棒的答案!我真的很欣赏关于为什么在工作负载繁重的情况下await 仍然会阻塞UI 的注释。 (2认同)

Seb*_*gel 11

您可以简单地使用Future<T>value工厂构造函数:

return Future<String>.value('Back to the future!');
Run Code Online (Sandbox Code Playgroud)


sil*_*udo 6

@ Fox32具有正确的答案,此外,我们需要提及“完成者的类型”,否则会出现异常

Exception received is type 'Future<dynamic>' is not a subtype of type 'FutureOr<List<Base>>

所以完成器的初始化将变成

var completer= new Completer<List<Base>>();