【发布时间】:2021-12-18 16:23:36
【问题描述】:
我正在编写一个基本程序来自学 Dart/Flutter。部分程序使用http.dart 包获取一些数据,http.get 命令返回Future 值。为了解压这个值,我需要使用await 命令,然后更改我的代码的执行顺序。在使用async/await 时,我无法弄清楚如何保留预期的执行顺序。我是新手,所以很感激我可能遗漏了一些明显的东西。
下面的代码示例1通过一系列函数使用async/await。这种方法或多或少给出了正确的输出顺序(main() 的结尾除外),但意味着(我认为)我需要一个 async build() 方法,这在 Flutter 中无效。
// Cascading async methods with local variables and await statements
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() async {
print('build: Before getName.');
String name = await _getName();
print('build: After getName.');
}
// Get some data, make some decisions
Future<String> _getName() async {
print('getName: before getData');
String name = await _getData();
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: returning body.data');
return name;
} else {
print('getName: returning Bob');
return 'Bob';
}
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
final data = await http.get(Uri.parse('http://www.google.co.uk'));
print('getData: After http get.');
return data.body;
}
由此产生的输出是(我已经截断了返回的 html 数据):
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
Main: end.
getData: After http get.
getName: after getData
getName: returning body.data
build: After getName. Name is: <html data>
下面的第二个代码示例使用全局变量在_getName() 方法中捕获数据,这样我就可以避免在build() 方法中使用async/await。这并没有给出正确的执行顺序或正确的输出。
// Use global variable to receive awaited data and avoid cascading async methods
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
String name = "";
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() {
print('build: Before getName.');
_getName();
print('build: After getName. Name is: $name');
}
// Get some data, make some decisions
Future<void> _getName() async {
print('getName: before getData');
String data = await _getData();
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: setting name = body.data');
name = data;
} else {
print('getName: setting name = Bob');
name = 'Bob';
}
return;
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
final data = await http.get(Uri.parse('http://www.google.co.uk'));
print('getData: After http get.');
return data.body;
}
此代码的输出如下所示。请注意,build() 方法在_getData 和_getName 之前完成,build() 中打印的名称在第 5 行为空。
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
build: After getName. Name is:
Main: end.
getData: After http get.
getName: after getData
getName: setting name = body.data
在下面的第三个示例中,我尝试使用.then 来确保每个函数中的代码仅在等待命令之后执行。我不认为这会起作用(而且它没有),因为我认为我在控制函数之间的流程时遇到了问题,而不是在控制函数内的流程时遇到了问题,但我认为我应该试一试,我正在抓紧到此为止。
// Use global variable to avoid using await in build() method
// Use .then to ensure that method actions follow await command
import 'dart:async';
import 'dart:math';
import 'package:http/http.dart' as http;
String name = ""; // Global variable for async data return
void main(List<String> arguments) {
print('Main: start.');
_build();
print('Main: end.');
}
// Draw some stuff, make some decisions
void _build() {
print('build: Before getName.');
_getName();
print('build: After getName. Name is: $name');
}
// Get some data, make some decisions
Future<void> _getName() async {
print('getName: before getData');
await _getData().then((data) {
print('getName: after getData');
double val = Random().nextDouble();
if (val < 0.5) {
print('getName: setting name = body.data');
name = data;
} else {
print('getName: setting name = Bob');
name = 'Bob';
}
});
return;
}
// Get the data via an http request
Future<String> _getData() async {
print('getData: Before http get.');
String value = "";
await http.get(Uri.parse('http://www.google.co.uk')).then((data) {
print('getData: After http get.');
value = data.body;
});
return value;
}
此代码的输出如下所示。与第二个示例一样,执行顺序不正确,build() 方法中打印的名称为空。
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
build: After getName. Name is:
Main: end.
getData: After http get.
getName: after getData
getName: setting name = Bob
理想情况下,程序的输出应该是:
Main: start.
build: Before getName.
getName: before getData
getData: Before http get.
getData: After http get.
getName: after getData
getName: setting name = Bob
build: After getName. Name is: Bob
Main: end.
如何编写我的代码,以便我可以使用http.get 方法并确保我的代码按我想要的顺序执行?我只想补充一点,我已经阅读了很多 stackoverflow 问题、flutter 文档和在线一般帮助,但到目前为止还没有找到任何可以回答我问题的内容。
或者我没有理解。 :D 抱歉,如果这是一个愚蠢的问题。在这方面我是菜鸟。
我应该补充一点,这个例子是对我正在编写的 Flutter 应用程序中的问题的简化(noughts and crosss)。这是在每次移动后检查获胜/平局,然后从数据库读取数据,更新结果并将它们写回数据库。它还会更新游戏状态以显示游戏结束。由 async/await 引起的问题是,当函数等待数据时,游戏状态没有更新,即使游戏结束,游戏也会继续处于“正在玩”状态。下面程序的伪代码(这有点杂乱无章,但希望它能说明问题)。
build() {
checkState(win, draw or continue?);
if (continue){
_humanMove();
_computerMove();
drawTheWidgets;
} else {
drawDifferentWidgets; // New game
}
}
void _humanMove() async {
processMove;
if (win/draw) {
await _updateResults;
updateGameState(game over);
}
}
void _computerMove() async {
processMove;
if (win/draw) {
await _updateResults;
updateGameState(game over);
}
}
results _updateResults() async {
await http.get data results in database;
updateWithResult;
await http.put data results in database;
}
【问题讨论】:
-
这能回答你的问题吗? What is a Future and how do I use it?
-
您明确声明
_build的返回类型为void而不是Future<void>。这可以防止调用者在完成时收到通知(_build因此是一个“即发即弃”功能)。如果你想让main等待_build完成,它必须返回一个Future,并且main必须await它。 -
我知道它已经过时了,但这必须是有史以来最好的视频之一,才能理解斯蒂芬的以下观点:你需要在得到结果之前渲染一些东西(例如占位符),然后再渲染一些不同的东西你有他们。 youtube.com/…
-
感谢@ChristopherMoore - 我已经阅读了有关 Futures 的信息,但这是一种非常简单的解释方式并且很有用。这让我想知道我是否应该使用全局变量并对其进行测试以查看它是否具有值(即承诺已履行),然后在数据可用(或读取失败)后让代码继续执行。虽然这感觉有点 hacky - 我总是被教导全局变量是不好的。这本质上是程序显示进度条或微调器时发生的情况吗?我认为这就是我需要做的(但没有微调器)。
-
谢谢@jamesdlin。我在原始帖子中添加了一些信息以澄清我的问题,并在下面回复了 Stephan 的帖子。我最初确实走这条路,但在我的 Flutter 应用程序中,这意味着我必须创建
build() async,这意味着它必须返回一个Future,这意味着它不再是build()方法的有效覆盖.
标签: flutter dart asynchronous async-await