The async
and await
是 Dart 中处理异步编程的机制。
异步操作让您的程序在等待时完成工作
以便完成另一项操作。
所以每当一个方法被标记为async
,您的程序不会因该方法的完成而暂停,只是假设它将在将来的某个时刻完成。
示例:错误地使用异步函数
下面的例子展示了异步函数的错误使用方式getUserOrder()
.
String createOrderMessage () {
var order = getUserOrder();
return 'Your order is: $order';
}
Future<String> getUserOrder() {
// Imagine that this function is more complex and slow
return Future.delayed(Duration(seconds: 4), () => 'Large Latte');
}
main () {
print(createOrderMessage());
}
如果运行上面的程序,它将产生以下输出 -
Your order is: Instance of '_Future<String>'
这是因为,由于该方法的返回类型被标记为Future https://api.dart.dev/stable/2.4.1/dart-async/Future-class.html,程序会将 is 视为异步方法。
为了获取用户的订单,createOrderMessage()
应该打电话getUserOrder()
并等待它完成。因为createOrderMessage()
不等待getUserOrder()
完成,createOrderMessage()
无法获取字符串值getUserOrder()
最终提供。
异步和等待
async 和await 关键字提供了一种声明性方式来定义异步函数并使用其结果。
所以每当你声明一个函数时async
,您可以使用关键字await
在任何方法调用之前,这将迫使程序在该方法完成之前不再继续进行。
例证
就你而言,fetchData()
函数被标记为async
你正在使用await
等待网络调用完成。
但在这儿fetchData()
返回类型为Future<void>
因此当你调用里面的方法时initState()
你必须这样做而不使用async/ await
since initState()
无法标记async
.
所以程序不会等待完成fetchData()
方法作为一个整体并试图显示本质上是null
。
自从你打电话以来setState()
数据加载到里面之后fetchData()
,屏幕刷新,一段时间后即可看到详细信息。
因此出现红色和黄色屏幕错误。
Solution
此问题的解决方案是您可以在屏幕上显示加载指示器,直到数据完全加载。
您可以使用bool
变量并根据该变量的值更改 UI。
例子 -
class _MyHomePageState extends State<MyHomePage> {
bool isLoading = false;
void initState() {
super.initState();
fetchData();
}
fetchData() async {
setState(() {
isLoading = true; //Data is loading
});
var cityUrl = "http://ip-api.com/json/";
var cityRes = await http.get(cityUrl);
var cityDecodedJson = jsonDecode(cityRes.body);
weatherCity = WeatherCity.fromJson(cityDecodedJson);
print(weatherCity.city);
var weatherUrl = "https://api.openweathermap.org/data/2.5/weather?q=" + weatherCity.city + "," +
weatherCity.countryCode +
"&appid=" +
//Calling open weather map's API key from apikey.dart
weatherKey;
var res = await http.get(weatherUrl);
var decodedJson = jsonDecode(res.body);
weatherData = WeatherData.fromJson(decodedJson);
print(weatherData.weather[0].main);
setState(() {
isLoading = false; //Data has loaded
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: isLoading ? Center(child : CircularProgressIndicator())
: Container(), //Replace this line with your actual UI code
);
}
}
希望这可以帮助!