我啥时候需要将异步函数的返回类型声明为未来对象?

Posted

技术标签:

【中文标题】我啥时候需要将异步函数的返回类型声明为未来对象?【英文标题】:When do I need to state the return type of an async function as a future object?我什么时候需要将异步函数的返回类型声明为未来对象? 【发布时间】:2019-10-24 11:57:44 【问题描述】:

我正在上一堂关于 Dart 异步编程的课程,但由于某种原因,我有点困惑。我想我理解了当异步函数可能需要一些时间时应该如何使用它的概念,因此您可以使用异步函数而不是阻塞和冻结您的应用程序,以便执行下一个代码块或方法以及何时执行异步函数完成或准备执行。 (如果我的理解有缺陷,请告诉我)

但是,我并没有真正理解 Future 部分。我知道它可以用作异步函数的返回类型,因为本质上你是说该函数现在返回一个未来对象,但让我们在它完成后回到它。但是我的导师有点让我困惑,有时她有一个未来作为返回类型,而另一次她没有把它说出来,它们都是异步函数。所以现在我很难理解什么时候有必要明确声明 Future 返回类型,即使它是无效的?另外,不只是使用异步并等待一个函数已经创建了一个未来的对象吗?非常感谢任何澄清,谢谢。

【问题讨论】:

你能举一些你遇到问题的代码示例 【参考方案1】:

是的,使用 async 关键字将使函数自动返回 Future。

即使对于 void 函数,显式声明函数的返回类型总是更好,否则,编译器会将函数解释为具有动态返回类型。

它还将帮助/使读者更容易了解函数的返回类型。

此外,您需要将对象包装在 Future 中的异步函数中,如下所示:

Future<Object> getObject() async 
  final object = await timeConsumingTask();
  return object;

如果你不包装就这样写:

Object getObject() async 
  final object = await timeConsumingTask();
  return object;

编译器抛出错误:Functions marked 'async' must have a return type assignable to 'Future'.

对于 void 函数,您似乎不必将返回类型包装在 Future 中,因此可以这样:

void doSomething() async 
  await timeConsumingTask();
  print('done');

【讨论】:

好的,所以本质上使用 future 和在函数不返回任何内容时仅使用 void 之间没有区别。只是为了澄清一下,每当我有一项耗时的任务并且我希望序列中的下一个函数在后台工作时继续运行时,我都应该使用异步函数,对吗?或者,如果我想例如使用 firebase 对用户进行身份验证,我是否也应该使用异步功能,所以我不想继续,直到用户得到正确身份验证?会使用异步跳过并转到下一个功能吗?我刚刚看到使用这种格式的那些函数。 您想使用 async/await 进行 firebase 身份验证,让我们说下一个屏幕await authenticateUser(); goToNextScreen; 'await' 关键字基本上意味着除非完成耗时的任务,否则不会执行下一行。当下一行依赖于需要以耗时方式获取的对象时,这尤其有用。【参考方案2】:

让我们考虑一个方法gatherNewsReports(),它只返回一个Future&lt;String&gt;。来自gatherNewsReportsFuture 的字符串值。实现gatherNewsReports

// Imagine this is a slow method. Takes time to finish
Future<String> gatherNewsReports() => Future.delayed(Duration(seconds: 1), () => news);

我们将遍历所有可能的方法调用与Futureasync 的组合。我要做的方法叫getDailyNewsDigest

选项一:

方法的一个定义可能与方法调用中给出的Futureasync 一样:

Future<String> getDailyNewsDigest() async 
  var str = await gatherNewsReports();
  print(str);
  return str;

请记住 gatherNewsReports() 返回一个 Future&lt;String&gt;。我们只是返回 gatherNewsReports 返回的任何内容。

用法:

main() async 
  await getDailyNewsDigest();
  print('call done');

输出:

<gathered news goes here>
call done

评论:这似乎是直截了当的。您只需调用该方法并等待它。请记住,当从 main 方法调用 getDailyNewsDigest 时,我们使用了 await 关键字。因此,除非我们通过1 second,即它需要Future 执行的持续时间。如果我们不使用 await 关键字,那么输出序列将被反转。

选项 2

方法的一个定义可能与在方法调用中给出的Futureasync NOT 一样:

Future<String> getDailyNewsDigest() 
  var str = await gatherNewsReports();
  print(str);
  return str;

这是无效的,因为您不能使用await 调用方法,在本例中为gatherNewsReports这是无效的

选项-3

我们定义的方法可能是在方法调用中给出的Future NOT GIVENasync。由于返回类型将是 void,我们将不会返回任何内容(注意:如果我们尝试返回除 Futurevoid 之外的任何内容,则会收到错误):

void getDailyNewsDigest() async 
  var str = await gatherNewsReports();
  print(str);

这是一个有效的方法定义。现在,由于该方法被声明为void,我们不能在main 方法上使用await。更新的main 方法。

main() async 
  getDailyNewsDigest(); // We can't await on it since return is void
  print('call done');

输出:

call done
<gathered news goes here>

正如您所见,由于没有await 就不会调用方法getDailyNewsDigest,因此输出是相反的。我们不再等待getDailyNewsDigest 完成。

选项-4

我们定义的方法可能是Future&lt;Void&gt;,而不是方法调用中给出的Future&lt;String&gt;async

Future<void> getDailyNewsDigest() async 
  var str = await gatherNewsReports();
  print(str);
  return;

现在在我们的 main 方法中,我们可以再次使用 await 关键字来调用 getDailyNewsDigest

main() async 
  await getDailyNewsDigest(); // We can await on it since return is Future<Void> but don't care on the output since it returns nothing
  print('call done');

这是我能想到的 4 种组合。

至于这个:

但是我的导师有点让我困惑,有时她的未来是 返回类型和另一次她没有说,他们都是 异步函数。

Future 声明一个方法意味着你可以等待它,用void 声明它意味着你不能在其中await。如果您不想对Future 的输出做任何事情,您当然可以选择不在Future 上使用await。如果您以不希望任何人依赖该方法的输出的方式编写代码,即,否则我们将从该方法返回的 Future 将自行处理 d,我们关心的是启动并继续我们的工作;在这种情况下,我们应该使用返回类型 void 来定义我们的方法。

【讨论】:

好的,但是当你说如果你用 void 声明它然后你不能等待它是什么意思。显然,一个带有 await 的 void 异步函数会等待 await 之后包含的部分在继续执行该函数之前发生?如果函数是用 async 定义的,这意味着序列中的下一个函数将开始工作,而 async 函数也正常工作?您能否详细解释一下使用异步函数如何影响执行的函数流? 如果声明方法,假设myMethodvoid myMethod() async,那么在main方法中不能做await myMethod(),只能做myMethod()。您当然可以从 INSIDE 方法执行await 功能,因为您声明它等待,但您永远不能使用await 从外部调用该方法。因此,如果您只调用此方法,那么 main 方法将按原样运行,而无需等待异步函数完成。 将方法声明为async 不会改变代码流。只有你await 才可以。查看选项1和3的输出。输出是相反的,唯一的区别是我没有从main方法中调用await

以上是关于我啥时候需要将异步函数的返回类型声明为未来对象?的主要内容,如果未能解决你的问题,请参考以下文章

vue3异步组件(defineAsyncComponentSuspense的使用)

我啥时候需要使用 super(props) 将 prop 传递给 react 组件的构造函数? [复制]

我啥时候需要处理(服务器-客户端)对象?

我啥时候应该使用 std::string / std::string_view 作为参数/返回类型

jdbc:我啥时候可以关闭啥

我啥时候必须声明 session_start();?