Flutter 异步与同步(3):Completer 详解

一、什么是 Completer?

Completer 是 Dart 异步库 dart:async 中的一个类,用于创建和控制一个 Future。它允许开发者手动完成一个 Future,使得 Future 的完成时机可以由代码逻辑自由控制,而不是由某个异步操作自动决定。

简单说,Completer 是 Future 的“控制器”。

二、Completer 基本概念

(1)核心作用

Completer 充当 Future 的控制器,可以:

  • 手动创建 Future
  • 在任意时机完成 Future(成功/失败)
  • 将回调式 API 转换为 Future 式 API

(2)核心方法

方法 说明
complete(value) 以成功状态完成 Future,并返回结果值
completeError(e) 以失败状态完成 Future,抛出异常
future 获取 Completer 控制的 Future 对象

三、基本使用示例

(1)创建并完成 Future

import 'package:flutter/material.dart';
import 'dart:async';

void main() {
  printMessage();
}

Future<String> createDelayedMessage() {
  final completer = Completer<String>();

  // 模拟异步操作
  Timer(const Duration(seconds: 2), () {
    completer.complete('异步操作完成');
  });

  return completer.future;
}

// 使用
void printMessage() async {
  final message = await createDelayedMessage();
  debugPrint(message); // 2秒后打印"异步操作完成"
}

2秒后输出打印:

flutter: 异步操作完成

(2)错误处理

import 'package:flutter/material.dart';
import 'dart:async';

void main() {
  calculate();
}

Future<int> divide(int a, int b) {
  final completer = Completer<int>();
  
  if (b == 0) {
    completer.completeError(ArgumentError('除数不能为0'));
  } else {
    completer.complete(a ~/ b);
  }
  
  return completer.future;
}

// 使用
void calculate() async {
  try {
    final result = await divide(10, 2);
    debugPrint(result as String?); // 5
    await divide(10, 0);
  } catch (e) {
    debugPrint(e as String?); // 打印错误: 除数不能为0
  }
}

2秒后输出打印:

flutter: 5
flutter: Invalid argument(s): 除数不能为0

四、实际应用场景

4.1 自定义异步操作

Future<String> loadData() {
  Completer<String> completer = Completer();

  // 假设是一些原生代码回调,或事件监听
  someAsyncEvent((result) {
    completer.complete(result);
  }, (error) {
    completer.completeError(error);
  });

  return completer.future;
}

4.1 将回调转换为 Future

// 原始回调式API
class LegacyAPI {
  void fetchData(void Function(String) callback) {
    Timer(const Duration(seconds: 1), () => callback('数据结果'));
  }
}

// 使用Completer包装为Future
Future<String> fetchDataAsFuture() {
  final completer = Completer<String>();
  LegacyAPI().fetchData((result) {
    completer.complete(result);
  });
  return completer.future;
}

// 使用
void useNewAPI() async {
  final data = await fetchDataAsFuture();
  print(data); // "数据结果"
}

4.3 多个异步操作协同

Future<String> waitForFirstSuccess(List<String> urls) {
  final completer = Completer<String>();
  
  for (final url in urls) {
    http.get(Uri.parse(url)).then((response) {
      if (!completer.isCompleted && response.statusCode == 200) {
        completer.complete(response.body);
      }
    }).catchError((_) {});
  }
  
  // 设置超时
  Timer(Duration(seconds: 10), () {
    if (!completer.isCompleted) {
      completer.completeError(TimeoutException('所有请求超时'));
    }
  });
  
  return completer.future;
}

4.4 结合 Timer 实现超时机制

Future<String> fetchDataWithTimeout() {
  Completer<String> completer = Completer();

  Timer timer = Timer(Duration(seconds: 5), () {
    if (!completer.isCompleted) {
      completer.completeError(TimeoutException("请求超时"));
    }
  });

  someAsyncFetch((result) {
    if (!completer.isCompleted) {
      completer.complete(result);
      timer.cancel();
    }
  }, (error) {
    if (!completer.isCompleted) {
      completer.completeError(error);
      timer.cancel();
    }
  });

  return completer.future;
}

五、注意事项

(1)完成状态检查

if (!completer.isCompleted) {
  completer.complete(value);
}

(2)内存泄漏

  • 确保 Completer 最终会被完成
  • 避免持有不再需要的 Completer 引用

(3)单次使用原则

  • 每个 Completer 只能完成一次
  • 重复调用 complete/completeError 会抛出 StateError

(4)Zone 处理

runZonedGuarded(() {
  final completer = Completer();
  // 异步操作
}, (error, stack) {
  if (!completer.isCompleted) {
    completer.completeError(error, stack);
  }
});

六、与普通 Future 对比

特性 Completer 普通 Future
控制权 手动控制完成时机 由异步操作自动控制
创建方式 显式构造 通过 async 函数或 Future 构造函数
使用场景 需要手动控制的异步流程 常规异步操作
灵活性
代码复杂度 较高 较低

Completer 为 Flutter 开发提供了更底层的 Future 控制能力,合理使用可以解决许多复杂的异步编程问题,但也要注意避免滥用导致代码难以维护。


参考:

Flutter 中的 Completer 详解:异步编程的利器Flutter 和 Dart 语言中,异步编程无处不在。F - 掘金


posted @ 2025-05-30 16:36  fengMisaka  阅读(164)  评论(0)    收藏  举报