异步/等待/然后在 Dart/Flutter 中

Posted

技术标签:

【中文标题】异步/等待/然后在 Dart/Flutter 中【英文标题】:Async/Await/then in Dart/Flutter 【发布时间】:2019-06-28 03:17:36 【问题描述】:

我有一个颤振应用程序,我在其中使用 SQFLITE 插件从 SQLite DB 获取数据。在这里,我面临一个奇怪的问题。据我了解,我们使用 async/await 或 then() 函数进行异步编程。 这里我有一个 db.query() 方法,它执行一些 SQL 查询以从数据库中获取数据。在这个函数获取数据之后,我们在 .then() 函数中做一些进一步的处理。但是,在这种方法中,我遇到了一些问题。从我调用这个 getExpensesByFundId(int fundId) 函数的地方,它似乎无法正确获取数据。它应该返回 Future> 对象,然后在数据可用时将其转换为 List 。但是当我打电话时它不起作用。

但是,我只是对它进行了一些实验,并在 db.query() 函数前面添加了“await”关键字,不知何故它刚刚开始正常工作。你能解释一下为什么添加 await 关键字可以解决这个问题吗?我想在使用 .then() 函数时,我们不需要使用 await 关键字。

这是我的代码:

Future<List<Expense>> getExpensesByFundId(int fundId) async 
    Database db = await database;

    List<Expense> expenseList = List();

   // The await in the below line is what I'm talking about

    await db.query(expTable,where: '$expTable.$expFundId = $fundId')
        .then((List<Map<String,dynamic>> expList)
      expList.forEach((Map<String, dynamic> expMap)
        expenseList.add(Expense.fromMap(expMap));
      );
    );
    return expenseList;
  

【问题讨论】:

【参考方案1】:

简单来说:

await 用于中断流程直到async 方法完成。 then 但不会中断流程(意味着将执行下一条指令),而是使您能够在 async 方法完成时运行代码。

在您的示例中,当您使用 then 时,您无法实现您想要的,因为代码没有“等待”并且 return 语句被处理并因此返回一个空列表。

当你添加await 时,你明确地说:'在我的Future 方法完成(即then 部分)之前不要再进一步。

您可以编写如下代码以仅使用 await 来获得相同的结果:

Future<List<Expense>> getExpensesByFundId(int fundId) async 
    Database db = await database;

    List<Expense> expenseList = List();

    List<Map<String,dynamic>> expList = await db.query(expTable,where: '$expTable.$expFundId = $fundId');
    expList.forEach((Map<String, dynamic> expMap) 
        expenseList.add(Expense.fromMap(expMap));
    );

    return expenseList;

您也可以选择仅使用then 部分,但您需要确保之后正确调用getExpensesByFundId

Future<List<Expense>> getExpensesByFundId(int fundId) async 
    Database db = await database;

    List<Expense> expenseList = List();
    
    return db.query(expTable,where: '$expTable.$expFundId = $fundId')
        .then((List<Map<String,dynamic>> expList)
      expList.forEach((Map<String, dynamic> expMap)
        expenseList.add(Expense.fromMap(expMap));
      );
    );


// call either with an await
List<Expense> list = await getExpensesByFundId(1);
// or with a then (knowing that this will not interrupt the process flow and process the next instruction
getExpensesByFundId(1).then((List<Expense> l)  /*...*/ );

【讨论】:

哇!!!多么美妙的解释。在我提交问题后,我发现该函数返回一个空列表,这导致了所有问题。但是你的回答更清楚了。但我还有一个问题。如果 await 关键字中断了流程,它不会冻结应用程序吗?我的意思是因为 Dart 在单线程上运行不会阻塞整个线程? 可以的。您必须了解事件循环的工作原理 (webdev.dartlang.org/articles/performance/event-loop) 以及 Isolates 在某些情况下如何为您提供帮助。 3 小时搜索以获得您在前 3 句话中所说的解释,但我也有 Javaland 提出的相同问题,所以 (await) 关键字会冻结应用程序直到它执行她的代码? 我不明白,所以如果 await 阻塞代码它的全部目的是什么?为什么不只使用也会阻塞的普通代码?我理解在结果来自未来之前您希望其他不相关代码运行的地方使用它。还是因为该功能仅作为未来可用,所以如果您在继续之前确实需要结果,则别无选择 await 仅与 async 函数一起使用,顾名思义,它们是异步运行的。因此,您可以选择是否要等待异步代码完成后再继续(使用await)。【参考方案2】:

补充以上答案。

Flutter Application 据说是一步步执行代码,其实不是这样的。 在应用程序的生命周期中会触发很多事件,例如 Click Event、Timers 等。一定有一些代码应该在后台线程中运行。

后台工作如何执行:

所以有两个Queues

    微任务队列 事件队列

Microtask Queue 运行不应由任何事件(单击、计时器等)运行的代码。它可以包含同步和异步工作。

Event Queue 在应用程序中发生任何外部点击事件(如 Click 事件)时运行,然后在事件循环内完成该块执行。

下图将详细解释执行将如何进行。

注意:在应用程序开发的任何给定点,微任务队列都会运行,然后只有事件队列能够运行。

【讨论】:

你能推荐一些我可以详细阅读的博客吗..【参考方案3】:

当类使用async 来使用await 时,它的简单逻辑是在你的函数中建立一个等待状态,直到你的数据被检索到显示。

示例:1) 就像您点击按钮时一样 2) 数据首先存储在数据库中,而不是 Future 函数用于检索数据 3) 将该数据移动到变量中,然后显示在屏幕中 4) 变量显示为您的增量关注/个人资料。

then是一步一步使用代码,将数据存储在变量中,然后进行下一步。

示例:如果我单击关注按钮,直到数据存储在变量中,它会不断检索一些要存储的数据并且不允许下一个函数运行,并且如果一个任务完成而不是移动到另一个任务。

与您的问题相同,我也在社交媒体颤振应用程序中进行实验,这是我的理解。我希望这会有所帮助。

【讨论】:

以上是关于异步/等待/然后在 Dart/Flutter 中的主要内容,如果未能解决你的问题,请参考以下文章

forEach() 循环的 Dart/Flutter 批处理?

在 dart/flutter 中执行多次从 firestore 获取数据的异步函数

为啥在 dart/flutter 中不等待 await 返回就执行代码?

Dart / Flutter:Isolate ***函数的异步行为

Dart / Flutter:错误“流已被收听。” fa“for循环”中的&&“等待”失败

颤振/飞镖:如何在飞镖 FFI 中使用异步回调?