颤振异步调用的最佳实践?

时间:2018-08-23 02:00:53

标签: flutter

我不希望每次重新构建小部件时都触发异步调用。 因此,我在initState中调用了异步函数。

  1. 在initState中调用异步

    ccache
  2. 使用FutureBuilder

    @override
    void initState() {
      someAsyncCall().then((result) {
        setState() {
         _result = result;
        }
      });
    }
    
    @override
    Widget build(BuildContext context) {
      if (_result == null) {
        reutrn new Container();
      }
    
      return new SomeWidget();
    }
    

两种解决方案都有副作用或不良做法吗?


我修改了演示应用程序,以解释为什么我要在initSate中调用异步。
1.打开应用,打印主画面
2.推动屏幕测试,打印测试,主
3.弹出测试,打印主画面

如果我在构建中调用异步函数,它将调用3次。
我想要的是,除非主要处理/弹出,否则我需要一次异步函数调用。
根据RémiRousselet的回答,在initState中调用异步是一种问题或错误。
那么,如何确保异步函数调用一次?

@override
void initState() {
  _future = someAsyncCall();
}

@override
Widget build(BuildContext context) {
  return new FutureBuilder(
    future: _future,
    builder: // do something
  );
}

我需要做类似

的事情吗
   @override
   Widget build(BuildContext context) {
    print("build main");
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Center(
        child: new Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            new RaisedButton(onPressed: () {
              Navigator.push(context, new MaterialPageRoute(builder: (context) {
                return new Test();
              }));
            }),
          ],
        ),
      )
    );
  }

 class Test extends StatelessWidget {

   @override
   Widget build(BuildContext context) {
     print("build Test");
     return new Scaffold(
       body:
       new FlatButton(onPressed: () {
         Navigator.pop(context);
       }, child: new Text("back"))
     );
   }

 }

3 个答案:

答案 0 :(得分:1)

虽然有些冗长,但FutureBuilder绝对是解决之道。

在initState中调用异步函数,然后在完成时执行setState会遇到一些问题。在大多数情况下,您不必担心错误捕获或处理加载时间。

请记住,Flutter希望initState是同步的。因此,虽然dart不会阻止您对同步函数进行异步调用,但flutter很可能会在未准备就绪时认为您的小部件已准备就绪。

答案 1 :(得分:0)

我会选择第一种样式,因为我可以控制等待时发生的情况。这取决于您要从应用程序中获得什么,我更喜欢清晰的“正在加载”消息,而不是不知道它是否卡住或正在加载。

谈到副作用,您可以看到他们对FutureBuilder的看法

  

此操作的副作用是,为FutureBuilder提供新的但已经完成的将来将导致ConnectionState.waiting状态中的单个帧。这是因为无法同步确定Future已经完成。

答案 2 :(得分:0)

我可以在异步函数的末尾做一个空的setState()。

这将使写入排队,以便仅在所有异步工作完成后才能看到它们的视觉效果。但是,队列将不可靠。应用中的其他内容可能会导致小部件重建,在这种情况下,您将看到单个写入的效果。

或者我可以将每个单独的分配包装到其自己的setState中:使用一个临时值等待getCustomValue();然后将其分配给setState内的值。

这将确保您看到每个增量状态更改的可视更新(假设异步任务之间有足够的时间来放置框架)。

我应该和谁一起去? 中间状态对用户有意义吗?具体来说,您是否要确保在执行此一系列操作时,应用程序的外观更新?相反,您是否要阻止中间状态显示给用户?

如果您希望用户看到中间状态,则可能希望将每个写入包装在setState调用中。如果要防止显示中间状态,则可能希望将写入实际上排入队列,并使它们全部在一系列异步操作的结尾自动发生。

在函数的末尾放置单个setState会使这两个世界变得更糟,因为根据应用程序中发生的其他情况,中间状态将是任意可见的。在最坏的情况下,中间状态可能是越野车,并且难以复制。

有更好的解决方案吗?

假设您要等待所有操作完成,然后以原子方式更新对象的状态,我建议让异步对象返回所有状态变化的表示。异步操作完成后,我将在单个setState调用中将所有更改同步提交给您的状态。这样可以自动改变您的状态,并确保应用程序的外观得到更新。

要考虑的另一个问题是,当异步操作挂起时,如果从树中删除窗口小部件会发生什么。理想情况下,您可以取消异步操作以节省资源,但是如果这不是一个选择,则可能要在提交更改之前检查State对象上已装入的布尔值。

来自:3951#issue