获取异步函数的 NULL 值(在使用 await 之后)然后更新为新值
Getting NULL value for async function (after using await) then updating to the new value
当我 运行 我的应用程序抛出很多错误时,我设备上的 red/yellow 错误屏幕会自动刷新并显示预期的输出。
从日志中我可以看到,首先我的对象 return 为 null,稍后会以某种方式更新并得到输出。
我最近开始Android开发(Flutter)
我尝试遵循一些在线指南并阅读有关异步响应的相关问题,但没有任何故障排除对我有帮助。
我最大的问题是我无法弄清楚到底是什么问题。
在我的 _AppState class:
void initState() {
super.initState();
fetchData();
}
fetchData() async {
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(() {});
}
预期输出(终端):
Mumbai
Rain
实际输出(终端):https://gist.github.com/Purukitto/99ffe63666471e2bf1705cb357c2ea32(实际错误超过了 Whosebug 的主体限制)
屏幕截图:
您必须等到 api 的数据到达。快速修复是在保存未来数据的变量上放置一个空合并运算符,如下所示:
String text;
fetchData() async {
//...
text = weatherData.weather[0].main ?? 'Waiting api response...';
//...
}
// in your build method
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Text(text), //this will render "Waiting api response" first, and when the api result arrive, it will change
),
);
}
否则,您可以使用 futureBuilder
小部件来实现。但是你必须将每个 api 放在不同的函数中,并将其更改为 Future,因此它具有 return 值。
Future fetchDataCity() async {
// your code
weatherCity = WeatherCity.fromJson(cityDecodedJson);
return weatherCity;
}
Future fetchDataWeather() async {
// your code
weatherData = WeatherData.fromJson(decodedJson);
return weatherData;
}
// in your build method
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: FutureBuilder(
future: fetchDataWeather(), // a previously-obtained Future or null
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState)
case ConnectionState.active:
case ConnectionState.waiting:
return Text('Awaiting result...'); //or a placeholder
case ConnectionState.done:
if (snapshot.hasError){
return Text('Error: ${snapshot.error}');
} else {
return Text('Error: ${snapshot.data}');
}
},
) //FutureBuilder
),
);
}
async
和await
是Dart中处理异步编程的机制。
Asynchronous operations let your program complete work while waiting
for another operation to finish.
因此,无论何时方法被标记为 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>'
这是因为,由于return类型的方法被标记为Future,程序将把它视为一个异步方法。
要获取用户的订单,createOrderMessage()
应该调用 getUserOrder()
并等待它完成。因为 createOrderMessage()
没有等待 getUserOrder()
完成,所以 createOrderMessage()
无法获取 getUserOrder()
最终提供的字符串值。
异步等待
async 和 await 关键字提供了一种声明方式来定义异步函数并使用它们的结果。
因此,无论何时将函数声明为 async
,都可以在任何方法调用之前使用关键字 await
,这将强制程序在方法完成之前不再继续。
案例
在您的例子中,fetchData()
函数被标记为 async
,并且您正在使用 await
等待网络调用完成。
但是这里 fetchData()
有一个 return 类型的 Future<void>
因此当你在 initState()
中调用方法时你必须这样做而不使用 async/ await
因为 initState()
不能被标记为 async
.
因此程序不会等待 fetchData()
方法作为一个整体完成,而是尝试显示本质上是 null
的数据。
并且因为你在fetchData()
中加载数据后调用setState()
,所以屏幕会刷新,一段时间后你可以看到详细信息。
因此红屏和黄屏错误。
解决方案
此问题的解决方案是您可以在屏幕上显示加载指示器,直到数据完全加载。
您可以使用 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
);
}
}
希望对您有所帮助!
当我 运行 我的应用程序抛出很多错误时,我设备上的 red/yellow 错误屏幕会自动刷新并显示预期的输出。
从日志中我可以看到,首先我的对象 return 为 null,稍后会以某种方式更新并得到输出。
我最近开始Android开发(Flutter)
我尝试遵循一些在线指南并阅读有关异步响应的相关问题,但没有任何故障排除对我有帮助。 我最大的问题是我无法弄清楚到底是什么问题。
在我的 _AppState class:
void initState() {
super.initState();
fetchData();
}
fetchData() async {
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(() {});
}
预期输出(终端):
Mumbai
Rain
实际输出(终端):https://gist.github.com/Purukitto/99ffe63666471e2bf1705cb357c2ea32(实际错误超过了 Whosebug 的主体限制)
屏幕截图:
您必须等到 api 的数据到达。快速修复是在保存未来数据的变量上放置一个空合并运算符,如下所示:
String text;
fetchData() async {
//...
text = weatherData.weather[0].main ?? 'Waiting api response...';
//...
}
// in your build method
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Text(text), //this will render "Waiting api response" first, and when the api result arrive, it will change
),
);
}
否则,您可以使用 futureBuilder
小部件来实现。但是你必须将每个 api 放在不同的函数中,并将其更改为 Future,因此它具有 return 值。
Future fetchDataCity() async {
// your code
weatherCity = WeatherCity.fromJson(cityDecodedJson);
return weatherCity;
}
Future fetchDataWeather() async {
// your code
weatherData = WeatherData.fromJson(decodedJson);
return weatherData;
}
// in your build method
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: FutureBuilder(
future: fetchDataWeather(), // a previously-obtained Future or null
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState)
case ConnectionState.active:
case ConnectionState.waiting:
return Text('Awaiting result...'); //or a placeholder
case ConnectionState.done:
if (snapshot.hasError){
return Text('Error: ${snapshot.error}');
} else {
return Text('Error: ${snapshot.data}');
}
},
) //FutureBuilder
),
);
}
async
和await
是Dart中处理异步编程的机制。
Asynchronous operations let your program complete work while waiting for another operation to finish.
因此,无论何时方法被标记为 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>'
这是因为,由于return类型的方法被标记为Future,程序将把它视为一个异步方法。
要获取用户的订单,createOrderMessage()
应该调用 getUserOrder()
并等待它完成。因为 createOrderMessage()
没有等待 getUserOrder()
完成,所以 createOrderMessage()
无法获取 getUserOrder()
最终提供的字符串值。
异步等待
async 和 await 关键字提供了一种声明方式来定义异步函数并使用它们的结果。
因此,无论何时将函数声明为 async
,都可以在任何方法调用之前使用关键字 await
,这将强制程序在方法完成之前不再继续。
案例
在您的例子中,fetchData()
函数被标记为 async
,并且您正在使用 await
等待网络调用完成。
但是这里 fetchData()
有一个 return 类型的 Future<void>
因此当你在 initState()
中调用方法时你必须这样做而不使用 async/ await
因为 initState()
不能被标记为 async
.
因此程序不会等待 fetchData()
方法作为一个整体完成,而是尝试显示本质上是 null
的数据。
并且因为你在fetchData()
中加载数据后调用setState()
,所以屏幕会刷新,一段时间后你可以看到详细信息。
因此红屏和黄屏错误。
解决方案
此问题的解决方案是您可以在屏幕上显示加载指示器,直到数据完全加载。
您可以使用 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
);
}
}
希望对您有所帮助!