You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 12:24:03