获取异步函数的NULL值(使用await之后),然后更新为新值

时间:2019-08-19 06:29:44

标签: android flutter dart async-await future

运行我的应用程序时,它会引发很多错误,并且设备上的红色/黄色错误屏幕会自动刷新并向我显示预期的输出。

从日志中,我可以看到我的对象首先以null返回,然后以某种方式更新并得到输出。

我最近启动了Android Dev(Flutter)

我尝试遵循一些在线指南,还阅读了有关异步响应的相关问题,但是没有故障排除对我有帮助。 我最大的问题是我无法弄清楚到底是什么问题。

在我的_AppState类中:

  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(实际错误已超过StackOverflow的主体限制)

屏幕截图: At run initial After a few seconds

2 个答案:

答案 0 :(得分:1)

asyncawait是一种在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,因此程序会将其视为异步方法。

要获取用户的订单,createOrderMessage()应致电getUserOrder()并等待其完成。因为createOrderMessage()不等待getUserOrder()完成,所以createOrderMessage()无法获得getUserOrder()最终提供的字符串值。


异步并等待

async和await关键字提供了一种声明性的方式来定义异步函数并使用其结果。

因此,每当您声明一个函数为async时,都可以在任何方法调用之前使用关键字await,这将迫使该程序在该方法完成之前无法继续进行操作。


关键点

在您的情况下,fetchData()函数被标记为async,并且您正在使用await等待网络调用完成。

但是这里fetchData()的返回类型为Future<void>,因此,当您在initState()内调用方法时,由于{{1} }不能标记为async/ await

因此,该程序不等待整个initState()方法的完成,而是尝试显示本质上为async的数据。 而且,由于在数据加载到fetchData()中之后调用了null,所以屏幕刷新了,一段时间后您可以看到详细信息。

出现红色和黄色屏幕错误。


解决方案

解决此问题的方法是,您可以在屏幕上显示加载指示器,直到数据完全加载为止。

您可以使用setState()变量并根据该变量的值更改UI。

示例-

fetchData()

希望这会有所帮助!

答案 1 :(得分:0)

您必须等到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,以便它具有返回值。

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
    ),
  );
}