为啥futureBuilder有时会卡在进度加载器上?

Posted

技术标签:

【中文标题】为啥futureBuilder有时会卡在进度加载器上?【英文标题】:Why is futureBuilder stuck on the progress loader sometimes?为什么futureBuilder有时会卡在进度加载器上? 【发布时间】:2020-06-11 02:00:43 【问题描述】:

我的整个 Flutter 应用中的 futureBuilders 存在问题。在每个屏幕中可以有 3 或 4 个部分,每个部分从特定的 API 端点加载数据。问题是有时,整个屏幕加载正常,有时一两个部分卡在进度条上!..我调试和搜索了很多,找不到真正的原因。 这让我讨厌颤抖:/

例如,这里是主页代码:

import 'package:carousel_pro/carousel_pro.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:soul_space/PODOs/categories.dart';
import 'package:soul_space/PODOs/event.dart';
import 'package:soul_space/PODOs/image.dart';
import 'package:soul_space/PODOs/space.dart';
import 'package:soul_space/custom_widgets/category-card.dart';
import 'package:soul_space/custom_widgets/error-card.dart';
import 'package:soul_space/custom_widgets/no-data.dart';
import 'package:soul_space/custom_widgets/search-widget.dart';
import 'package:soul_space/services/categories-service.dart';
import 'package:soul_space/services/events-service.dart';
import 'package:flutter/foundation.dart';
import 'package:soul_space/services/spaces-service.dart';
import '../custom_widgets/home-row.dart';
import 'package:async/async.dart';

class HomeTab extends StatefulWidget 
  HomeTab(Key key, this.title, this.mainScaffoldKey) : super(key: key);

  final GlobalKey<ScaffoldState> mainScaffoldKey;
  final String title;

  @override
  HomeTabState createState() => HomeTabState();


class HomeTabState extends State<HomeTab> with AutomaticKeepAliveClientMixin 
  Future _homeImagesFuture;
  final AsyncMemoizer _categoriesMemoizer = AsyncMemoizer();
  Future _categoriesFuture;
  final AsyncMemoizer _eventsMemoizer = AsyncMemoizer();
  Future _eventsFuture;
  final AsyncMemoizer _spacesMemoizer = AsyncMemoizer();
  Future _spacesFuture;

  var isLoading = false;

  final List<String> rowsTitles = ['New Events', 'Featured Spaces'];
  List colors = [
    Colors.red[500],
    Colors.teal[300],
    Colors.yellow[500],
    Colors.orange[300],
    Colors.red[400],
    Colors.blue
  ];
  Random random = new Random();

  @override
  void initState() 
    super.initState();
    _categoriesFuture = fetchCategories();
    _eventsFuture = fetchEvents();
    _spacesFuture = fetchSpaces();
    _homeImagesFuture = getHomeImages();
  

  fetchCategories() async 
    return this._categoriesMemoizer.runOnce(() async 
      return await getCategories();
    );
  

  fetchEvents() async 
    return this._eventsMemoizer.runOnce(() async 
      return await fetchAllEvents('approved');
    );
  

  fetchSpaces() async 
    return this._spacesMemoizer.runOnce(() async 
      return await fetchAllSpaces('approved');
    );
  

  String _homeSliderImages;
  Future<List<String>> getHomeImages() async 
    List<LoadedAsset> assets = await fetchSliderImages();
    List<String> temp = [];
    for (var i = 0; i < assets.length; i++) 
      temp.add(assets[i].link);
    

    return temp;
  

  @override
  Widget build(BuildContext context) 
    super.build(context);
    debugPrint('REBUILD:' + DateTime.now().toString());
    return Scaffold(
        body: SingleChildScrollView(
      child: Column(
        children: <Widget>[
          Stack(
            children: [

              SearchBar(scaffoldKey: widget.mainScaffoldKey),
            ],
          ),
          Padding(
            padding: const EdgeInsets.only(left: 10.0, right: 10.0),
            child: FutureBuilder(
                future: _eventsFuture,
                builder: (BuildContext context, AsyncSnapshot snapshot) 
                  if (snapshot.connectionState == ConnectionState.done) 
                    debugPrint('***************************DONE1');
                    if (snapshot.hasError) 
                      debugPrint(snapshot.error.toString());
                      return ErrorCard(
                        isSmall: true,
                      );
                     else if (snapshot.hasData) 
                      final List<Event> events = snapshot.data;
                      return HomeRow(
                          title: rowsTitles[0],
                          events: events,
                          route: '/allEvents');
                     else 
                      return NoDataCard(
                        isSmall: true,
                      );
                    
                   else 
                    debugPrint('***************************LOADER1');
                    return Container(
                        margin: EdgeInsets.symmetric(vertical: 0.0),
                        height: 250.0,
                        child: Center(child: RefreshProgressIndicator()));
                  
                ),
          ),
          Padding(
            padding: const EdgeInsets.only(left: 10.0, right: 10.0),
            child: FutureBuilder(
                future: _spacesFuture,
                builder: (BuildContext context, AsyncSnapshot snapshot) 
                  if (snapshot.connectionState == ConnectionState.done) 
                    debugPrint('***************************DONE2');

                    if (snapshot.hasError) 
                      debugPrint(snapshot.error.toString());
                      return ErrorCard(
                        isSmall: true,
                      );
                     else if (snapshot.hasData) 
                      final List<Space> spaces = snapshot.data;
                      return HomeRow(
                          title: rowsTitles[1],
                          spaces: spaces,
                          route: '/allSpaces');
                     else 
                      return NoDataCard(
                        isSmall: true,
                      );
                    
                   else 
                    debugPrint('***************************LOADER2');
                    return Container(
                        margin: EdgeInsets.symmetric(vertical: 0.0),
                        height: 250.0,
                        child: Center(child: RefreshProgressIndicator()));
                  
                ),
          )
        ],
      ),
    ));
  

  @override
  bool get wantKeepAlive => true;

这是一个从 API 解析 json 数据的服务函数示例:

Future<List<Event>> fetchAllEvents(String approval, int id = -1) async 
  var url = globals.apiUrl +
      '/events/' +
      (id != -1 ? id.toString() + '/' : '') +
      _getQueryParams(false, approval);
  dio.Response<String> response = await get(url);
  debugPrint('***************************GET SUCCEES1');
  var temp = await compute(parseEvents, response.data);
  debugPrint('***************************PARSE SUCCEES1');
  return temp;

.
.
List<Event> parseEvents(String responseBody) 
  final parsedJson = json.decode(responseBody);
  var list = parsedJson['data'] as List;
  if (list != null) 
    List<Event> eventsList = list.map((i) => Event.fromJson(i)).toList();
    return eventsList;
   else 
    return [];
  


此外,这里有好的和坏的构建日志的例子,希望它们有助于总结原因:

显示进度条时打印'LOADER' get 返回时打印'GET SUCCESS' 计算函数解析json成功时打印'PARSE SUCCESS' 正确加载小部件时会打印“DONE”

有什么解决办法吗?

编辑

我按照接受的答案中的建议删除了“计算”功能并对其进行了修复,但不确定为什么有时它会毫无例外地失败

【问题讨论】:

错误日志中没有PARSE SUCCESS1,所以未来状态仍在加载中。 @Kahou,是的,但解释是什么? 【参考方案1】:

执行没有超出这条线:

  var temp = await compute(parseEvents, response.data);

可能parseEvents 正在扔东西。

注意你是如何从不使用try/catch的。您必须编写代码来捕获异常。 parseEvents 可能会引发异常(格式错误的 json?,空 json->null?),由于您没有捕捉到它,因此您不知道发生了什么。

我会首先尝试摆脱compute,然后调用parseEvents 来查看异常是否因为compute 而被静默忽略。然后我会将try/catch 放在 parseEvents 函数中,并逐渐让所有执行路径都包含try/catch,包括async 函数和await 调用。

【讨论】:

好建议。我尝试添加 try/catch 并保持计算 => 仍然是相同的问题而没有任何异常。然后我尝试删除计算,对其进行了多次测试,似乎问题消失了,有什么线索吗?在将其标记为正确之前,我将进行更多测试。但我想了解为什么计算会毫无例外地挂起。这会影响应用的性能吗? 您是否在parseEvents 的所有行周围都放置了try/catch?我从未使用过compute,应该阅读一下如何在使用它时正确处理错误。

以上是关于为啥futureBuilder有时会卡在进度加载器上?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 JS WebSocket 在 sparkjava 中会卡在 CONNECTING 上?

Android 网络调用有时会卡在 IOException :java.net.SocketTimeoutException: timeout

win7系统为啥会卡在35%不动?

java 有一行语句是调用外部接口的,但该接口不稳定,有时候请求时间会很长,程序就会卡在这一行很长时间

CentOS6.4_x86_开关机查看

为什么下载小电影会卡在99% ?你懂的