“ FutureOr”的目的是什么?

时间:2019-12-06 12:29:40

标签: dart

Dart提供了一个FutureOr类,该类允许编写:

FutureOr<int> future;

future = 42; // valid
future = Future.value(42); // also valid

如果可以同步读取该值,我认为FutureOr可以消除事件循环所引起的不必要的延迟。

但事实并非如此,如以下所示:

import 'dart:async';

void main() async {
  print('START');
  futureOrExample();
  print('END');
}

void futureOrExample() async {
  FutureOr<int> futureOr = 42;
  print('before await');
  await futureOr;
  print('end await');
}

打印:

START
before await
END
end await

当我期望的时候:

START
before await
end await
END

在这种情况下,为什么FutureOr(或更普遍的await 42)以这种方式工作?

类似地,在这种情况下FutureOr的目的是什么,因为它产生的结果与Future相同?

我知道我可以使用SynchronousFuture来达到预期的结果,但是我只是想了解FutureOr的用途。

4 个答案:

答案 0 :(得分:2)

使用Dart 2中引入的FutureOr是为了让您在现有Dart 1 API为方便起见允许同一件事的情况下提供价值或未来可以静态输入。

典型示例为Future.thenFuture<T>上的签名为Future<R> then<R>(FutureOr<R> action(T value), {Function onError})

想法是您可以对同步或异步的未来价值采取行动。最初有一个then函数接受一个同步回调,一个chain函数接受一个异步回调,但是使用起来非常烦人,而且在Dart 1风格良好的情况下,API简化为一个then方法,该方法接受一个返回dynamic的函数,然后检查它是否是将来。

在Dart 1中,很容易允许您返回值或未来。 Dart 2不太宽大,因此引入了FutureOr类型以允许现有API继续工作。如果我们是从头开始编写API的,那么我们可能会做其他事情,但是将现有的异步代码库迁移到完全不同的事情是不可行的,因此FutureOr类型是类型级别的黑客。

await操作最初也定义为可在任何对象上运行,早于FutureOr存在。为了保持一致和简化代码,将await e评估为非未来的e将在将来包装该值并等待。这意味着只有一个快速且可重用的值检查(如果不包装它,它是否是一个未来),然后其余代码相同。只有一个代码路径。 如果await在非Future值上同步工作,则必须有一条通过await的同步代码路径,以及一条等待将来的异步路径。例如,当编译为JavaScript时,这可能会使代码大小加倍(或者更糟的是,如果在同一控制流中有更多await,那么天真的实现可能会呈指数级增长)。即使您仅通过同步调用延续函数来避免这种情况,也可能会使某些读者感到困惑,await不会引入异步间隙。周围的错误可能导致比赛条件或发生错误的顺序。

因此,在FutureOr之前的原始设计是使所有await操作实际上处于等待状态。

FutureOr的引入并没有改变这种推理方式,即使这样做了,在人们期望他们的代码实际给出的地方, not 等待也是一个重大改变。其他微任务运行的时间。

答案 1 :(得分:-1)

TL; DR

if (futureOr is int)
  print(futureOr + 9);
else
  print((await futureOr) + 9);

文档指出:

  

FutureOr<Object>等效于Object

这意味着以下内容将产生您想要的内容:

void futureOrExample() {
  FutureOr<int> futureOr = 42;
  print('before');
  print((futureOr as int) + 9);
  print('end');
}

但是

如果futureOr实际上是FutureFutureOr<int> futureOr = 42),这显然将失败。

话虽如此,如果您不确定该值是否为await,则应该明确FutureOr Future

为什么

Dart将始终以异步方式运行此命令,因为它是一个await语句(我不确定Dart现在如何解释它,但您已经在问题中指出了这一点)。

该代码基本上等同于以下代码段:

void futureOrExample() async {
  Future.sync(() {
    FutureOr<int> futureOr = 42;
    print('before await');
    return futureOr;
  }).then((value) {
    print('end await');
  });
}

解决方案

在类型上使用条件。这意味着您将能够在可能的情况下使用同步代码,而在其他情况下await则非常简单:

if (futureOr is int)
  print(futureOr + 9);
else
  print((await futureOr) + 9);

允许您测试此行为的示例:

Future<void> futureOrExample() async {
  FutureOr<int> futureOr = Random().nextBool() ? 42 : Future.value(42);
  print('before');
  if (futureOr is int)
    print(futureOr + 9);
  else
    print((await futureOr) + 9);
  print('end');
}

结论

也许Dart不能独自处理它令人失望,但是老实说,我喜欢这种方式,因为您可以确定await将始终将代码放入事件循环中。您已经确认FutureOr总是这样,但是我想您可以在dart-lang/language提出问题。
无论如何,我非常喜欢这个问题-谢谢:)

答案 2 :(得分:-1)

讨论迟到了。 更新我对 Dart 的理解 - 请原谅我的 C++/JS-ish 方法。

似乎这对单例初始化很有用。考虑以下事项:

import 'dart:async';

class AClass {
  static String _info = '';
  
  static FutureOr<String> get info async {
    if (_info.isEmpty) {
      print('--> is empty...');
      _info = await Future.delayed(Duration(seconds:2), 
                                   () => "I'm alive!!");
    }
    else {
      print('--> not empty');
    }
    
    return _info;
  }
}

Future<void> main() async {
  String info = await AClass.info;
  print('Fist call: ' + info);
  
  info = await AClass.info;
  print('Second call: ' + info);
}

它按预期工作 - 在任何一种情况下,无论 _info 成员是否已实例化,getter 都会返回一个有效字符串。

如果我也在 getter 中使用 Future<String> 说明符,它也能正常工作。当前的实现使 FutureOr 看起来更像是一个自我文档化的练习(可以返回一个 Future<String> 或一个 String...)

但是,即使 await 当前始终锁定执行,未来的更新也可能允许它按预期工作,在这种情况下,使用 FutureOr 构造会预期更新。

(旁白:我想这个例子可以使用一个包含 Optional 成员的 _info 来压缩,但那是一个不同的练习......)

答案 3 :(得分:-2)

await关键字始终锁定函数执行。

写作:

await 42

等效于:

await Future.value(42)

原因是:

  • 这是await在Javascript中的工作方式
  • 这使await的行为保持一致。

现在,FutureOr的目的是什么?

FutureOr从未打算作为一种可能使await同步的方法。

相反,它是Future的实现细节。

没有FutureOr,编写以下内容将不会编译:

Future(() {
  return 42; // compile error, not a Future
});

Future<int> future;
future.then((value) {
  return value * 2; // compile error, not a Future
});

相反,我们必须像这样将所有值包装在Future.value中:

Future<int> future;
future.then((value) {
  return Future.value(value * 2);
});
相关问题