在Dart中,是否可以创建自己的Future来从方法中返回?或者你必须始终从Dart异步库中的内置Future返回一个?
我想定义一个函数,它始终返回Future<List<Base>>
,无论实际上是执行异步调用(文件读取/ajax等)还是仅仅获取本地变量,如下所示:
List<Base> aListOfItems = ...;
Future<List<Base>> GetItemList(){
return new Future(aListOfItems);
}
在Dart中,是否可以创建自己的Future来从方法中返回?或者你必须始终从Dart异步库中的内置Future返回一个?
我想定义一个函数,它始终返回Future<List<Base>>
,无论实际上是执行异步调用(文件读取/ajax等)还是仅仅获取本地变量,如下所示:
List<Base> aListOfItems = ...;
Future<List<Base>> GetItemList(){
return new Future(aListOfItems);
}
如果你需要创建一个 Future,可以使用 Completer
。请参阅文档中的 Completer
类。以下是示例:
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;
}
但是大多数情况下,您不需要使用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..
});
}
还有一个有关文件输入输出的示例:
Future<List<String>> readCommaSeperatedList(file){
return file.readAsString().then((text) => text.split(','));
}
查看这篇博客文章获取更多技巧。
您可以简单地使用Future<T>value
工厂构造函数:
return Future<String>.value('Back to the future!');
本文总结了多种实现方法。
你的方法可以是任何形式,但为了举例方便,我们假设你的方法如下:
int cubed(int a) {
return a * a * a;
}
目前您可以这样使用您的方法:
int myCubedInt = cubed(3); // 27
然而,您希望您的方法像这样返回一个Future
:
Future<int> myFutureCubedInt = cubed(3);
或者更实际地像这样使用:
int myCubedInt = await cubed(3);
Future<int> cubed(int a) {
return Future(() => a * a * a);
}
Future<int>
,然后将旧函数的工作作为匿名函数传递给Future
构造函数。Future.value
或Future.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);
}
async
方法会自动返回Future
,因此您只需将方法标记为async
并更改返回类型即可:
Future<int> cubed(int a) async {
return a * a * a;
}
async
和 await
结合使用,但并不是必须的。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);
}
使用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;
}
这个例子与上面的命名构造函数解决方案类似。它处理加法中的错误,并以正常方式完成future。
使用future并不能保证不会阻塞用户界面(UI)(也就是主隔离区)。从函数返回future只是告诉Dart将任务安排在事件队列的末尾。如果该任务十分密集,则当事件循环调度运行它时,仍然会阻塞UI。
如果您有一个密集型任务需要在另一个隔离区运行,则必须在其上启动一个新的隔离区运行它。当该任务在其他隔离区上完成时,它将作为future返回消息,您可以将其作为函数的结果传递。
许多标准Dart IO类(例如File
或HttpClient
)具有委托工作给系统的方法,因此不会在您的UI线程上执行密集的工作。因此,这些方法返回的futures是安全的,不会阻塞您的UI。
@Fox32提供了正确的答案,除此之外,我们还需要提到Completer的类型,否则会出现异常。
收到的异常类型为 'Future<dynamic>' 不是类型为 'FutureOr<List<Base>>' 的子类型
因此,completer的初始化应该变成:
var completer = new Completer<List<Base>>();
虽然不是针对所提出的问题的答案,但有时我们可能希望等待一个闭包:
flagImage ??= await () async {
...
final image = (await codec.getNextFrame()).image;
return image;
}();
String? _data;
Future<String> load() async {
// use preloaded data
if (_data != null) return Future<String>.value(_data);
// load data only once
String data = await rootBundle.loadString('path_to_file');
_data = data;
return data;
}
.value('回到未来!')
被标记为死代码。 - Steve Gelman