Flutter中Future.wait未等待异步函数,报空值NoSuchMethodError求助
你遇到的NoSuchMethodError: '<Unexpected Null Value>'以及日志顺序颠倒的问题,核心原因是没有正确等待Future.wait()返回的异步任务完成,就直接执行了后续代码,再加上documentIDToName函数存在返回null的风险,才导致了这些问题。
具体原因拆解
异步代码执行顺序问题:
你在调用Future.wait()后,用了.then()处理结果,但没有用await等待整个Future.wait()完成。.then()是异步回调,会在后台任务完成后才执行,但主线程不会暂停,会直接往下走,所以此时_itemCtgName和_itemSubCtgName还是未初始化的null,自然会先打印出null,之后.then()里的赋值代码才会执行。空值未处理:
documentIDToName函数中,如果查询不到对应的数据,_extractedName会保持为null直接返回,这也可能导致后续使用时出现空值错误。
修复方案
1. 正确等待Future.wait()完成
把Future.wait()用await包裹,让代码暂停直到所有异步任务完成,再去获取结果和打印。同时调整变量声明逻辑,避免在异步回调中使用未初始化的变量:
Stream<List<Item>> _itemDataProvider = DatabaseService().getAllItems; List<Item> _extractedList = await _itemDataProvider.first; for (int i = 0; i < _extractedList.length; i++) { print("---> Inside for loop " + _extractedList[i].price.toString()); try { // 用await等待Future.wait完成,拿到所有结果 final values = await Future.wait([ DBHelperFtns().documentIDToName( FirebaseFirestore.instance.collection('itemCtg'), "itemCtgID", "name", _extractedList[i].itemCtgID, ), DBHelperFtns().documentIDToName( FirebaseFirestore.instance.collection('itemSubCtg'), "itemSubCtgID", "name", _extractedList[i].itemSubCtgID, ), ]); // 直接从结果中赋值,无需依赖then回调 final _itemCtgName = values[0]; final _itemSubCtgName = values[1]; print(" _itemCtgName: " + _itemCtgName.toString()); print(" _itemSubCtgName: " + _itemSubCtgName.toString()); } catch (e) { print("ERROR : $e"); } }
2. 优化documentIDToName函数,处理空数据情况
修改函数,确保无论是否查询到数据,都返回非空值(或者抛出异常明确错误),同时用async/await替代.then()让代码更易读:
Future<String> documentIDToName( CollectionReference passedCollection, String dbKeyNamOfID, String dbKeyNamOfName, String passedID, ) async { print("----> Inside documentIDToName ftn"); final querySnapshot = await passedCollection .where(dbKeyNamOfID, isEqualTo: passedID) .get(); if (querySnapshot.docs.isNotEmpty) { // 处理可能的空值,返回默认文本 return querySnapshot.docs[0].data()[dbKeyNamOfName]?.toString() ?? 'Unknown Category'; } else { // 两种选择:要么返回默认值,要么抛出异常让上层处理 // 方案1:返回默认值 return 'Unknown Category'; // 方案2:抛出异常(更适合需要明确错误的场景) // throw Exception("No document found matching ID: $passedID"); } }
进阶优化:批量并行处理(可选)
如果你的列表项很多,逐个await会导致串行执行,效率较低。可以把所有项的异步任务收集起来,用Future.wait()批量并行处理:
Stream<List<Item>> _itemDataProvider = DatabaseService().getAllItems; List<Item> _extractedList = await _itemDataProvider.first; // 为每个item创建异步任务 final processingFutures = _extractedList.map((item) async { print("---> Processing item " + item.price.toString()); final values = await Future.wait([ DBHelperFtns().documentIDToName( FirebaseFirestore.instance.collection('itemCtg'), "itemCtgID", "name", item.itemCtgID, ), DBHelperFtns().documentIDToName( FirebaseFirestore.instance.collection('itemSubCtg'), "itemSubCtgID", "name", item.itemSubCtgID, ), ]); return { 'itemPrice': item.price, 'category': values[0], 'subCategory': values[1], }; }).toList(); // 等待所有任务并行完成 final results = await Future.wait(processingFutures); // 处理所有结果 for (var result in results) { print("Item Price: ${result['itemPrice']}, Category: ${result['category']}, Subcategory: ${result['subCategory']}"); }
这种方式所有异步任务同时执行,能大幅提升处理速度。
内容的提问来源于stack exchange,提问作者Faizan Kamal

