Dart中的Future / async / await

时间:2018-03-28 15:02:59

标签: asynchronous async-await dart flutter

我有一个函数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方法?

6 个答案:

答案 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关键字的目的是声明性地将一个函数标记为异步并使用它的结果。

  • 要定义异步函数,请在函数主体之前添加异步
  • await关键字仅在异步函数中起作用。

因此,每当您尝试从标记为异步的函数获取输出时,它都别无选择,只能返回Future。请看下面的示例以进一步说明。

  • 首先,您有一个可以进行一些计算的函数
  • 第二,您有一个简单的函数,可以通过执行简单的http get请求从API获取数据。
  • 最后是另一个函数,它将处理一些数据并打印一些值。

    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种方式处理它。
    1. 获取返回的Future并使用'then()'函数像promise一样处理它,因此无需标记父对象 功能异步
    2. 将父函数标记为async并使用await处理返回的对象,以强制该函数等待结果。
    3. 在异步函数末尾使用异步函数的输出调用所需函数。这将允许主要 函数继续非依赖函数,同时等待 异步函数的结果和一个异步函数的结果 结果它可以在最后进入另一个函数并执行它 接收到的数据。

答案 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)

thenawait不同。 await将在此处停止程序,直到完成Future任务。但是then不会阻止程序。 then任务之后完成后,将执行Future中的块。

如果您希望程序等待Future任务,请使用await。如果您希望程序继续运行并且Future任务是在“后台”执行操作,请使用then

关于您的问题,我建议重新设计。执行其他地方的构造函数所需的加载资产和其他async事务。这些任务完成后,请调用构造函数。