Dart/Flutter如何异步将大量数据映射为列表避免主线程卡顿
问题原因说明
Dart Isolate之间传递的消息必须是可序列化的纯数据,不能包含闭包、函数、上下文依赖对象。你之前直接将依赖外部csvData的匿名过滤逻辑传入compute,才会触发闭包非法的报错。而asyncMap没有效果是因为它依然运行在主线程的事件循环中,只是拆分了任务执行时机,大量计算依然会阻塞UI线程。
优化方案
1. 先降低过滤逻辑时间复杂度
你当前O(n*m)(11000*1000=1100万次比对)的逻辑本身性能极差,先把csvData预处理成二级索引,把平均比对次数降到个位数:
// 主线程预处理csv为索引,仅1000次循环,无性能压力 Map<String, List<String>> csvMatchIndex = {}; for (var element in csvData) { final indexKey = '${element.cardName}|${element.setName}'; csvMatchIndex.putIfAbsent(indexKey, () => []); csvMatchIndex[indexKey]!.add(element.cardNumber); }
2. 抽离过滤逻辑为顶级函数
过滤逻辑必须是无上下文依赖的顶级函数或者静态类函数,才能传入compute执行:
// 独立的顶级过滤函数,所有依赖参数显式传入 List<Map<String, dynamic>> filterCards(List<Map<String, dynamic>> cardsList, Map<String, List<String>> matchIndex) { final result = <Map<String, dynamic>>[]; for (final card in cardsList) { final cardKey = '${card['card_name']}|${card['set_name']}'; final prefixList = matchIndex[cardKey]; if (prefixList == null) continue; final cardNumber = card['number'] as String; if (prefixList.any((prefix) => cardNumber.startsWith(prefix))) { result.add(card); } } return result; }
3. 调用compute执行过滤
注意要先把数据库返回的懒加载Iterable转成普通List后再传入Isolate,迭代器本身持有数据库上下文不能跨Isolate传递:
// 先全量读取原始数据为可序列化的List final cardsRawList = cardsRaw.toList(); // 异步执行过滤,完全不卡主线程 final filteredCards = await compute(filterCards, [cardsRawList, csvMatchIndex]);
额外说明
- 如果你的csv元素是自定义类,要么转成基础类型集合传递,要么给类实现可序列化逻辑
- 不要把数据库连接、流、闭包这类不可序列化对象传入Isolate
- 后续如果要把过滤后的Map转成类实例,也可以直接把映射逻辑写在上面的
filterCards函数里,一次性在Isolate中完成所有计算
内容的提问来源于stack exchange,提问作者Ahmad Sattout
相关产品推荐
相关产品推荐

