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的用途。
答案 0 :(得分:2)
使用Dart 2中引入的FutureOr
是为了让您在现有Dart 1 API为方便起见允许同一件事的情况下提供价值或未来可以静态输入。
典型示例为Future.then
。 Future<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)
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
实际上是Future
(FutureOr<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);
});