【问题标题】:Dart, how to create a future to return in your own functions?Dart,如何在你自己的函数中创建一个返回的未来?
【发布时间】:2013-08-27 17:40:32
【问题描述】:

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

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

List<Base> aListOfItems = ...;

Future<List<Base>> GetItemList(){

    return new Future(aListOfItems);

}

【问题讨论】:

    标签: asynchronous dart future


    【解决方案1】:

    如果您需要创建未来,可以使用Completer。请参阅文档中的Completer class。这是一个例子:

    Future<List<Base>> GetItemList(){
      var completer = new Completer<List<Base>>();
        
      // At some time you need to complete the future:
      completer.complete(new List<Base>());
        
      return completer.future;
    }
    

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

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

    使用完成程序可以使代码变得非常复杂。您可以简单地使用以下代码,因为 then() 也会返回 Future

    Future<List<Base>> GetItemList(){
      return aFuture.then((a) {
        // Do something..
      });
    }
    

    或者文件io的例子:

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

    请参阅this blog post 了解更多提示。

    【讨论】:

      【解决方案2】:

      您可以简单地使用Future&lt;T&gt;value 工厂构造函数:

      return Future<String>.value('Back to the future!');
      

      【讨论】:

      • 也许我做错了什么,但我将 .value('Back to the future!') 标记为死代码。
      • 但是在接收回调的位置,我们如何处理 对象,在你的情况下是 (回到未来!)?
      • 这个解决方案对我来说是最简单的。我确实
        返回 Future>.value(new List());
      【解决方案3】:

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

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

      起点

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

      int cubed(int a) {
        return a * a * a;
      }
      

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

      int myCubedInt = cubed(3);  // 27
      

      但是,您希望您的方法返回 Future,如下所示:

      Future<int> myFutureCubedInt = cubed(3);
      

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

      int myCubedInt = await cubed(3);
      

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

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

      最基本的解决方案是使用Future的生成构造函数。

      Future<int> cubed(int a) {
        return Future(() => a * a * a);
      }
      

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

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

      Futures 可以完成一个值或一个错误。因此,如果您想明确指定这些选项中的任何一个,您可以使用 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);
      }
      

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

      Future<int> cubed(int a) {
        return Future.value(a * a * a);
      }
      

      解决方案 3:异步方法

      async 方法会自动返回 Future,因此您只需标记方法 async 并更改返回类型,如下所示:

      Future<int> cubed(int a) async {
        return a * a * a;
      }
      

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

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

      Future<int> cubed(int a) async {
        return await cubedOnRemoteServer(a);
      }
      

      或者这是使用Future.then 语法的同一件事:

      Future<int> cubed(int a) async {
        return cubedOnRemoteServer(a).then((result) => result);
      }
      

      解决方案 4:完成者

      使用Completer 是最低级别的解决方案。只有当您有一些上述解决方案无法涵盖的复杂逻辑时,您才需要这样做。

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

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

      关于阻止 UI 的说明

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

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

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

      另见

      【讨论】:

      • 这是一个绝妙的答案!我非常感谢关于为什么 await 在繁重的工作负载情况下仍会阻塞 UI 的说明。
      【解决方案4】:

      @Fox32 有正确答案,除了我们需要提及完成者的类型,否则我们会得到异常

      Exception received is type 'Future&lt;dynamic&gt;' is not a subtype of type 'FutureOr&lt;List&lt;Base&gt;&gt;

      所以完成者的初始化会变成

      var completer= new Completer&lt;List&lt;Base&gt;&gt;();

      【讨论】:

      • 但是当控件从该函数返回时,我们如何处理(接收)对象“List”?
      【解决方案5】:

      不完全是给定问题的答案,但有时我们可能想await 一个闭包:

          flagImage ??= await () async {
            ...
            final image = (await codec.getNextFrame()).image;
            return image;
          }();
      

      我认为它确实隐含地创造了一个未来,即使我们没有在任何地方传递它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-10-12
        • 2017-04-05
        • 2012-09-26
        • 1970-01-01
        • 2021-12-07
        • 2023-03-15
        相关资源
        最近更新 更多