Flutter中使用await for读取Stream时后续代码不执行问题排查
问题核心原因
- 你传入的
driverWithinRangeStream是Firestore实时监听类的持续推送流,这类流的设计目的是持续同步数据库的最新变化,只要没有被主动取消订阅,就会一直推送数据,永远不会主动触发流的done完成事件。而await for循环的退出前提是必须接收到流的完成信号才会跳出循环、执行后续代码。因为这个流永远不会进入完成状态,所以循环外的代码自然完全没有执行机会。 - 循环内部的文档遍历逻辑存在异步等待缺失:
forEach是同步执行的方法,传入的async回调不会被forEach等待执行,forEach调用完成就会立刻推进到下一行代码,根本不会等每个文档的距离计算、列表添加的操作跑完。哪怕流能正常结束,最终生成的列表数据也会是不完整的。
对应修正方案
- 针对持续流不主动结束的问题:如果需求是只读取首批推送的、符合范围的司机列表,不需要长期持续监听流的后续更新,可以给流加
take(1)截断逻辑,只取第一次推送的数据后就主动结束订阅,让await for可以正常跳出循环。 - 针对forEach不等待异步操作的问题:把
forEach替换为普通的for-in遍历,配合await等待每个文档的处理逻辑执行完成,保证所有文档都处理完毕、数据全部加入列表后,才进入下一轮循环。
修正后的可运行代码
Future<List<Map<String, dynamic>>> getNearestDriver( Stream<List<DocumentSnapshot>> driverWithinRangeStream, GeoPoint source) async { print("Get Nearest Driver Called"); List<Map<String, dynamic>> driverAvailableList = []; // 加take(1)截断:只取第一次推送的数据后主动结束流,触发await for的退出条件 await for (List<DocumentSnapshot> documentList in driverWithinRangeStream.take(1)) { // 替换forEach为普通for循环,逐行等待每个文档的处理完成 for (DocumentSnapshot document in documentList) { String driverIDVar = document.data['driverid']; GeoPoint pos = document.data['position']['geopoint']; var point = geo.point(latitude: pos.latitude, longitude: pos.longitude); double distanceVar = point.distance(lat: source.latitude, lng: source.longitude); int distanceInMeter = (distanceVar * 1000).round(); Map<String, dynamic> newDriverAvailable = { "driverId": driverIDVar, "distance": distanceInMeter }; driverAvailableList.add(newDriverAvailable); } print(driverAvailableList); // 循环内的数据打印 } print(driverAvailableList); // 现在这行代码可以正常执行,拿到完整列表 return driverAvailableList; }
补充说明:使用裸
listen方式监听流时,因为listen本身是同步注册监听的方法,注册完回调就会立刻执行后续代码,根本不会等流把所有数据推送完成,所以才会出现listen调用完成后代码就提前执行的问题。对于需要等待流读取完成再返回结果的场景,优先用带截断逻辑的await for实现,不需要手动处理流的订阅取消、等待完成的复杂逻辑。
内容的提问来源于stack exchange,提问作者Alias。
相关产品推荐
相关产品推荐

