Flutter Dart中Future函数提前于stream.listen返回的问题求助
解决Flutter中Future提前返回Stream未完成的问题
问题根源
scanNetwork4VT函数提前返回的核心原因:循环中调用stream.listen()是异步订阅操作,函数不会等待这些Stream的所有事件处理完成,就直接执行到末尾的return resultJson.toString(),导致Stream内的HTTP请求、JSON解析等逻辑还没执行完,结果就被返回了。
解决方法
方案1:用await for串行处理每个Stream
把stream.listen()替换成await for循环,它会自动遍历Stream的所有事件直到Stream关闭,确保当前子网的所有扫描逻辑完成后,再处理下一个子网:
Future<String> scanNetwork4VT({ required String commandString, }) async { print("scanNetwork4VT called"); String wifiName = ""; // 获取WIFI名称 wifiName = await _initNetworkInfo(); // 检查WIFI连接 if (wifiName == "") { return Future.error("WIFI not connected!", StackTrace.current); } // 获取本地网络接口和地址 List<String> myInternetAddresses = []; for (var interface in await NetworkInterface.list( includeLinkLocal: false, )) { print('== Interface: ${interface.name} =='); for (var addr in interface.addresses) { myInternetAddresses.add(addr.address); } } if (myInternetAddresses.isEmpty) { return Future.error("error", StackTrace.current); } // 请求头配置 Map<String, String> myHeaders = { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br', 'Content-type': 'application/json', 'Connection': 'keep-alive', 'User-Agent': "PostmanRuntime/7.29.2", }; List<dynamic> resultJson = []; final int port = 80; // 遍历所有网络接口对应的子网 for (String foundIPaddress in myInternetAddresses) { String subnet = foundIPaddress.substring(0, foundIPaddress.lastIndexOf('.')); print("subnet of $subnet"); final stream = NetworkAnalyzer.discover2(subnet, port); // 用await for替代listen,等待当前stream所有事件处理完成 await for (NetworkAddress addr in stream) { if (addr.exists) { try { print('trying GET REST: ${addr.ip}'); myHeaders['Host'] = "google.com"; Uri myURI = Uri( scheme: "http", host: addr.ip.toString(), port: 80, path: "", queryParameters: {"ascdd": ""}); http.Response response = await http.get(myURI, headers: myHeaders); if (response.statusCode == 200) { try { final jsonResponse = json.decode(Uri.decodeFull(response.body)); if (jsonResponse.isNotEmpty) { print('OK'); myClass myCustomClass = myClass.fromJson(jsonResponse); if (myCustomClass.nodeId.isNotEmpty) { myCustomClass.ipaddress = addr.ip.toString(); resultJson.add(myCustomClass); } } } catch (e) { print("jsondecode error: $e"); } } else { print('response.statusCode: ${response.statusCode}'); } } catch (e) { print("http get error: $e"); } } } print("当前子网扫描完成: $subnet"); } return resultJson.toString(); }
方案2:并行处理所有Stream(提升扫描效率)
如果希望同时扫描所有子网而非逐个串行处理,可以将每个Stream的处理逻辑包装成Future,用Future.wait()等待所有任务完成:
Future<String> scanNetwork4VT({ required String commandString, }) async { // 前面的WIFI检查、地址获取逻辑和方案1一致 List<dynamic> resultJson = []; final int port = 80; // 存储所有子网扫描的Future任务 List<Future<void>> scanTasks = []; for (String foundIPaddress in myInternetAddresses) { String subnet = foundIPaddress.substring(0, foundIPaddress.lastIndexOf('.')); print("subnet of $subnet"); final stream = NetworkAnalyzer.discover2(subnet, port); // 将当前子网的扫描逻辑包装为Future,加入任务列表 scanTasks.add(() async { await for (NetworkAddress addr in stream) { if (addr.exists) { // 和方案1相同的HTTP请求、结果处理逻辑 } } print("当前子网扫描完成: $subnet"); }()); } // 等待所有子网的扫描任务完成 await Future.wait(scanTasks); return resultJson.toString(); }
额外优化点
- 去掉不必要的空安全断言:原代码中
myInternetAddresses初始化为空列表,无需用List<String>?和!断言,直接定义为List<String> myInternetAddresses = []即可。 - 并行方案需注意线程安全:多个任务同时修改
resultJson可能存在竞态问题,建议用synchronized包或手动加锁避免数据异常。
内容的提问来源于stack exchange,提问作者LiveRock
相关产品推荐
相关产品推荐

