在Dart中如何等待异步下载任务完成或捕获对应错误?
问题根因
你当前的异步逻辑存在的问题是:response.stream.listen属于非阻塞调用,仅负责注册流的事件回调,不会等待流处理完成就会继续执行后续代码。因此startDownloading函数在完成请求发起、回调注册逻辑后就会直接结束返回,不会等待文件下载、本地写入的完整流程执行完毕,才会出现解压阶段提前启动的问题。
解决方案
使用Dart标准库中的Completer手动控制startDownloading函数的Future完成时机,等下载流程(包含流处理、文件写入)完全结束(成功/失败)后再标记Future完成,即可让外层的await正常等待整个下载阶段结束。
调整后的完整代码
import 'dart:async'; // 确保导入async库,Completer在该库中 /// Starts download procress Future<void> startDownloading() async { print("startDownloading…"); // 创建Completer实例,手动控制Future的完成 final Completer<void> downloadCompleter = Completer(); _state = DownloadState.downloading; _progress = 0; notifyListeners(); /// Database string url final databaseUrlForInstantData = "https://XXX"; try { final request = Request('GET', Uri.parse(databaseUrlForInstantData)); final StreamedResponse response = await Client().send(request); final contentLength = response.contentLength; // == Start from zero _progress = 0; notifyListeners(); /// The currently downloaded file as an array of bytes List<int> bytes = []; response.stream.listen( /// = Closure listener for newly downloaded bytes (List<int> newBytes) { bytes.addAll(newBytes); final downloadedLength = bytes.length; if (contentLength == null) { _progress = 0; } else { _progress = downloadedLength / contentLength; } notifyListeners(); print( 'Download in progress $_progress%: ${bytes.length} bytes so far'); }, /// = Download successfully completed onDone: () async { _state = DownloadState.downloaded; notifyListeners(); /// The resulting local copy of the database final file = await _getDownloadedZipFile(); // == Write to file await file.writeAsBytes(bytes); print('Download complete: ${bytes.length} bytes'); // 下载+写入完成,标记Completer正常完成 downloadCompleter.complete(); }, /// = Download error onError: (e) { _state = DownloadState.error; _error = e.message; print('Download error at $_progress%: $e'); // 下载出错,标记Completer返回错误 downloadCompleter.completeError(e); }, cancelOnError: true, ); } // == Catches potential error catch (e) { _state = DownloadState.error; _error = 'Could not download the databse: $e'; print('Download error at $_progress%: $e'); // 请求阶段出错,标记Completer返回错误 downloadCompleter.completeError(e); } // 返回Completer关联的Future,会等待上面的complete/completeError调用才会结束 return downloadCompleter.future; }
补充说明
调整后外层await startDownloading()会一直阻塞到整个下载流程完全结束:
- 下载成功并写完本地文件后,才会继续执行后续解压逻辑
- 下载任意阶段出现错误,也会走完错误处理逻辑后抛出异常,被外层的try catch捕获后再进入解压阶段,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Stéphane de Luca
相关产品推荐
相关产品推荐

