我有一个函数loadData
,可以从文件中加载一些文本:
Future<String> loadAsset() async {
return await rootBundle.loadString('assets/data/entities.json');
}
loadString
方法来自Flutter SDK,并且是异步的。
然后在另一种方法中调用loadAsset
方法,我必须将其标记为async
,因为loadAsset
是异步的,我需要使用await
:
Future<List<Entity>> loadEntities() async {
String jsonData = await loadAsset();
return parseData(jsonData);
}
parseData
方法不是异步的,它接收String
,解析它,并返回一个对象列表:
List<Entity> parseData(String jsonString) {
...
}
但由于loadEntities
必须标有async
,因此需要返回Future
,但实际上,它不是Future
,因为我使用await
,等待loadAsset
方法完成,然后使用结果调用parseData
函数。
这很容易变成async
来电的雪球,因为使用loadEntities
的每种方法也必须标记为async
。
另外,我不能在类构造函数中使用loadEntities
,因为构造函数应该标记为async
,这在Dart中是不允许的。
我在Dart中使用async/await
模式错了吗?我怎么能在类构造函数中使用loadEntities
方法?
答案 0 :(得分:13)
不,异步具有传染性,无法从异步返回同步执行。
async
/ await
只是methodThatReturnsFuture().then(...)
使用async
标记方法只是为了允许您在其正文中使用await
。如果没有async
,您仍需要返回Future
来调用代码,以便在loadAsset()
的结果可用后执行。
答案 1 :(得分:8)
您可以直接使用异步调用返回的Future。这看起来像这样:
class HasAsync {
HasAsync() {
asyncFunction().then((val) {
print(val);
});
}
Future<int> asyncFunction() async {
int val = await otherFunction();
return val;
}
}
你不能在非异步功能中使用await。
当你用“颤动”标记这一点时,我猜这是在一个颤动的应用程序中。如果是这种情况,请查看docs for FutureBuilder - 这可能有助于您尝试做的事情。
答案 2 :(得分:3)
我知道我可能对您来说太晚了,无法利用此答案,但无论如何,我正在写它,希望有人能对它有所帮助。这是我的两分钱。
与您初次尝试弄清什么是异步编程以及如何使用异步编程时,我的思维过程相同。
由于问题与Flutter有关,因此我将使用dart进行解释。
首先,让我们深入了解在异步编程中使用async等待的基本目的。
根据flutter文档,async和await关键字的目的是声明性地将一个函数标记为异步并使用它的结果。
因此,每当您尝试从标记为异步的函数获取输出时,它都别无选择,只能返回Future。请看下面的示例以进一步说明。
最后是另一个函数,它将处理一些数据并打印一些值。
void processInfo(){
calculateStuff();
Future<Map> decodedData = getDataFromInternet();
getDataProcessed(decodedData);
}
因此,在同步编程中,这意味着所有三个功能将一个接一个地执行。但是,让我们说第二个函数getDataFromInternet()是异步调用的。一个简单的实现如下所示。
Future<Map> getDataFromInternet() async {
http.Response response = await http.get(this._apiPath);
Map decodedData;
if (response.statusCode != 200)
print("invalid response. cannot proceed!");
else {
decodedData = jsonDecode(response.body);
}
return decodedData;
}
因此需要上述函数才能返回将来。问题是为什么? 这很简单。在这种情况下,这是因为由于我们要返回某些东西,并且在执行return语句时,并且此时'get request'中的数据可能或可能不可用。
因此,该函数将返回处于完全状态或不完全状态的Future类型结果。
那么我们如何处理这个结果呢?事实上,这可以通过3种方式完成。
1。方法一-兑现承诺
因此,在此示例中,一旦getDataFromInternet()函数返回Future结果,您就需要该Future结果的过程,例如如何处理javascript中的promise。请参考下面的代码示例。
void getDataProcessed(Future<Map> data) {
print('getting data from future map');
data.then((d) {
print(d);
});
}
2。方法二-将父函数标记为异步(传染性)
void processInfo() async{
calculateStuff();
//now you can simply await that result and process the result rather
//than handling a Future<Map> result in this case.
//Note: it is not required to use future variable because we are awaiting
//for result
Map decodedData = await getDataFromInternet();
getDataProcessed(decodedData);
}
因此在这种情况下,getDataProcessed()函数将类似于以下内容。
void getDataProcessed(Map data) {
//this will simply print the data object which is complete result which is by
//no way is a promise object
print(data);
}
3。方法三-在同步函数中使用异步方法的结果(非传染性方式)
在这种情况下,processInfo()函数将稍有变化,即getDataProcessed()将不再在此方法中被调用,并且将类似于以下内容。
void processInfo(){
calculateStuff();
getDataFromInternet();
}
我们可以使用getDataFromInternet()函数的结果来调用getDataProcessed()函数,而不是在processInfo()函数中调用getDataProcessed(),这意味着我们不必将processInfo()标记为异步,我们可以完成执行getDataFromInternet()方法后,处理getDataProcessed()方法。以下代码示例演示了如何执行此操作。
void getDataFromInternet() async {
http.Response response = await http.get(this._apiPath);
Map decodedData;
if (response.statusCode != 200)
print("invalid response. cannot proceed!");
else {
decodedData = jsonDecode(response.body);
}
//in this case, since we are awaiting for get results response and the
//function is not expected to return anything the data type passed into
//getDataProcessed() function now will be of type Map rather than being type
//Future<Map> . Thus allowing it to be a synchronous function and without
//having to handle the future objects.
getDataProcessed(decodedData);
}
void getDataProcessed(Map data) {
//this will simply print the data object which is complete result which is by
//no way is a promise object
print(data);
}
所以修改这个长答案,
- async / await只是标记异步功能的声明方式
- 当调用异步函数时,可以用3种方式处理它。
- 获取返回的Future并使用'then()'函数像promise一样处理它,因此无需标记父对象 功能异步
- 将父函数标记为async并使用await处理返回的对象,以强制该函数等待结果。
- 在异步函数末尾使用异步函数的输出调用所需函数。这将允许主要 函数继续非依赖函数,同时等待 异步函数的结果和一个异步函数的结果 结果它可以在最后进入另一个函数并执行它 接收到的数据。
答案 3 :(得分:1)
只需更正: 异步方法不会传染。 Test构造函数调用异步方法sayHello。 这演示了如何在不允许将ctor声明为异步时使ctor进行异步调用。 main方法被简单地标记为async,以便在sayHello完成之前应用程序不会关闭。这不是一个内在要求。
class Test
{
Test()
{
sayHello();
}
void sayHello() async
{
await Future.delayed(Duration(seconds: 4) , () => print("Hello"));
print("We said hello");
}
}
void main() async
{
doTest();
// wait for hello to be output
await Future.delayed(Duration(seconds: 6) , () {});
print("Ending");
}
void doTest()
{
Test();
}
答案 4 :(得分:0)
最简单的构造函数是 Future()
,它接受一个函数并返回一个与函数返回类型匹配的未来。稍后函数异步运行,future 以函数的返回值结束。以下是使用 Future()
的示例:
void main() {
final myFuture = Future(() {
print('Creating the future.'); // Prints second.
return 12;
});
print('Done with main().'); // Prints first.
}
要使用已完成的值,您可以使用 then()
。这是每个 future 上的一个实例方法,您可以使用它来注册一个回调,以便在 future 以一个值完成时。你给它一个函数,它接受一个与未来类型匹配的参数。一旦 future 以一个值结束,你的函数就会被调用这个值。
void main() {
Future.delayed(
const Duration(seconds: 3),
() => 100,
).then((value) {
print('The value is $value.'); // Prints later, after 3 seconds.
});
print('Waiting for a value...'); // Prints first.
}
有关更多信息,请参阅https://medium.com/dartlang/dart-asynchronous-programming-futures-96937f831137
答案 5 :(得分:-1)
then
和await
不同。 await
将在此处停止程序,直到完成Future
任务。但是then
不会阻止程序。 then
任务之后完成后,将执行Future
中的块。
如果您希望程序等待Future
任务,请使用await
。如果您希望程序继续运行并且Future
任务是在“后台”执行操作,请使用then
。
关于您的问题,我建议重新设计。执行其他地方的构造函数所需的加载资产和其他async
事务。这些任务完成后,请调用构造函数。