dart-async-programming

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

dart-async-programming

Dart异步编程

Goal

目标

Implements robust, idiomatic asynchronous Dart code using
Future
and
Stream
APIs. Manages concurrency, stream processing, and error handling while adhering to strict Dart async patterns, memory safety guidelines, and optimal execution flow.
使用
Future
Stream
API编写健壮、符合惯用规范的Dart异步代码。在遵循严格的Dart异步模式、内存安全指南和最优执行流程的前提下,管理并发、流处理和错误处理。

Decision Logic

决策逻辑

When determining the appropriate asynchronous pattern, evaluate the following decision tree:
  • Single Asynchronous Operation: Use
    Future<T>
    with
    async
    and
    await
    .
  • Multiple Independent Operations: Use
    Future.wait
    to execute concurrently.
  • Sequence of Asynchronous Events: Use
    Stream<T>
    .
    • Finite/Sequential Data (e.g., File I/O, Network Responses): Consume using
      await for
      .
    • Infinite/Event-Driven Data (e.g., UI Events): Use
      listen()
      on a Broadcast Stream.
  • Custom Stream Generation:
    • Simple Generation: Use
      async*
      and
      yield
      .
    • Complex State/Event Management: Use
      StreamController
      .
确定合适的异步模式时,参考以下决策树进行评估:
  • 单异步操作: 搭配
    async
    await
    使用
    Future<T>
  • 多个独立操作: 使用
    Future.wait
    并发执行。
  • 异步事件序列: 使用
    Stream<T>
    • 有限/顺序数据(例如文件I/O、网络响应): 使用
      await for
      消费。
    • 无限/事件驱动数据(例如UI事件): 在广播流(Broadcast Stream)上调用
      listen()
  • 自定义流生成:
    • 简单生成: 使用
      async*
      yield
    • 复杂状态/事件管理: 使用
      StreamController

Instructions

使用说明

  1. Implement Single Futures with Error Handling Always use
    async
    /
    await
    syntax instead of
    .then()
    . Wrap operations in
    try-catch
    blocks to handle exceptions gracefully.
    dart
    Future<String> fetchUserData(String userId) async {
      try {
        final result = await api.getUser(userId);
        return result.name;
      } catch (e, stackTrace) {
        // Handle specific errors or rethrow
        logError('Failed to fetch user: $e', stackTrace);
        throw UserFetchException(e.toString());
      }
    }
  2. Execute Independent Futures Concurrently When multiple asynchronous operations do not depend on each other, initiate them concurrently using
    Future.wait
    .
    dart
    Future<UserProfile> loadCompleteProfile(String userId) async {
      try {
        final results = await Future.wait([
          api.getUser(userId),
          api.getUserPreferences(userId),
          api.getUserHistory(userId),
        ]);
        
        return UserProfile(
          user: results[0] as User,
          preferences: results[1] as Preferences,
          history: results[2] as History,
        );
      } catch (e) {
        throw ProfileLoadException('Failed to load profile components: $e');
      }
    }
  3. Consume Streams Sequentially Use
    await for
    to consume streams when you need to process events sequentially and wait for the stream to close.
    dart
    Future<int> calculateTotal(Stream<int> numberStream) async {
      int total = 0;
      try {
        await for (final number in numberStream) {
          total += number;
        }
      } catch (e) {
        logError('Stream processing failed: $e');
        return -1;
      }
      return total;
    }
  4. Generate Streams using Generators For straightforward sequential event generation, use
    async*
    and
    yield
    .
    dart
    Stream<int> generateCountdown(int from) async* {
      for (int i = from; i >= 0; i--) {
        await Future.delayed(const Duration(seconds: 1));
        yield i;
      }
    }
  5. Manage Complex Streams with StreamController When manually controlling stream state, instantiate a
    StreamController
    . You MUST ensure the controller is closed when no longer needed to prevent memory leaks.
    dart
    class DataManager {
      final StreamController<DataEvent> _controller = StreamController<DataEvent>.broadcast();
    
      Stream<DataEvent> get dataStream => _controller.stream;
    
      void addData(DataEvent event) {
        if (!_controller.isClosed) {
          _controller.add(event);
        }
      }
    
      void dispose() {
        _controller.close();
      }
    }
  6. Apply Stream Transformations and Timeouts Use built-in stream methods to handle errors, timeouts, and transformations before consumption.
    dart
    Stream<String> processNetworkStream(Stream<List<int>> byteStream) async* {
      final safeStream = byteStream
          .handleError((e) => logError('Network error: $e'))
          .timeout(
            const Duration(seconds: 5),
            onTimeout: (sink) {
              sink.addError(TimeoutException('Stream timed out'));
              sink.close();
            },
          )
          .transform(utf8.decoder)
          .transform(const LineSplitter());
    
      await for (final line in safeStream) {
        if (line.isNotEmpty) yield line;
      }
    }
  7. Context Checkpoint STOP AND ASK THE USER: If the user requests stream implementation but does not specify if the stream will have multiple listeners (e.g., UI state) or a single listener (e.g., file reading), ask them to clarify before implementing
    StreamController
    or
    StreamController.broadcast()
    .
  8. Validate-and-Fix After generating asynchronous code, perform a validation pass:
    • Check: Are there any raw
      .then()
      or
      .catchError()
      chains? Fix: Convert to
      async
      /
      await
      and
      try-catch
      .
    • Check: Are multiple independent
      await
      calls made sequentially? Fix: Combine them using
      Future.wait
      .
    • Check: Is a
      StreamController
      instantiated without a corresponding
      close()
      method in a disposal lifecycle? Fix: Add the
      close()
      call.
  1. 实现带错误处理的单个Future 始终使用
    async
    /
    await
    语法而非
    .then()
    。将操作包裹在
    try-catch
    块中以优雅处理异常。
    dart
    Future<String> fetchUserData(String userId) async {
      try {
        final result = await api.getUser(userId);
        return result.name;
      } catch (e, stackTrace) {
        // Handle specific errors or rethrow
        logError('Failed to fetch user: $e', stackTrace);
        throw UserFetchException(e.toString());
      }
    }
  2. 并发执行独立的Future 当多个异步操作互不依赖时,使用
    Future.wait
    并发启动它们。
    dart
    Future<UserProfile> loadCompleteProfile(String userId) async {
      try {
        final results = await Future.wait([
          api.getUser(userId),
          api.getUserPreferences(userId),
          api.getUserHistory(userId),
        ]);
        
        return UserProfile(
          user: results[0] as User,
          preferences: results[1] as Preferences,
          history: results[2] as History,
        );
      } catch (e) {
        throw ProfileLoadException('Failed to load profile components: $e');
      }
    }
  3. 顺序消费Stream 当你需要顺序处理事件并等待流关闭时,使用
    await for
    消费流。
    dart
    Future<int> calculateTotal(Stream<int> numberStream) async {
      int total = 0;
      try {
        await for (final number in numberStream) {
          total += number;
        }
      } catch (e) {
        logError('Stream processing failed: $e');
        return -1;
      }
      return total;
    }
  4. 使用生成器生成Stream 对于简单的顺序事件生成,使用
    async*
    yield
    dart
    Stream<int> generateCountdown(int from) async* {
      for (int i = from; i >= 0; i--) {
        await Future.delayed(const Duration(seconds: 1));
        yield i;
      }
    }
  5. 使用StreamController管理复杂Stream 当需要手动控制流状态时,实例化
    StreamController
    。你必须确保不再需要控制器时将其关闭,以防止内存泄漏。
    dart
    class DataManager {
      final StreamController<DataEvent> _controller = StreamController<DataEvent>.broadcast();
    
      Stream<DataEvent> get dataStream => _controller.stream;
    
      void addData(DataEvent event) {
        if (!_controller.isClosed) {
          _controller.add(event);
        }
      }
    
      void dispose() {
        _controller.close();
      }
    }
  6. 应用Stream转换和超时设置 在消费流之前,使用内置的流方法处理错误、超时和转换。
    dart
    Stream<String> processNetworkStream(Stream<List<int>> byteStream) async* {
      final safeStream = byteStream
          .handleError((e) => logError('Network error: $e'))
          .timeout(
            const Duration(seconds: 5),
            onTimeout: (sink) {
              sink.addError(TimeoutException('Stream timed out'));
              sink.close();
            },
          )
          .transform(utf8.decoder)
          .transform(const LineSplitter());
    
      await for (final line in safeStream) {
        if (line.isNotEmpty) yield line;
      }
    }
  7. 上下文检查点 停止并询问用户: 如果用户请求实现流,但未指定流会有多个监听器(例如UI状态)还是单个监听器(例如文件读取),在实现
    StreamController
    StreamController.broadcast()
    之前请先询问用户明确需求。
  8. 验证与修复 生成异步代码后,执行一次验证检查:
    • 检查: 是否存在原生的
      .then()
      .catchError()
      链?修复: 转换为
      async
      /
      await
      try-catch
      写法。
    • 检查: 是否有多个独立的
      await
      调用顺序执行?修复: 使用
      Future.wait
      合并它们。
    • 检查: 实例化的
      StreamController
      是否在销毁生命周期中没有对应的
      close()
      方法?修复: 添加
      close()
      调用。

Constraints

约束条件

  • DO use
    async
    /
    await
    instead of raw
    .then()
    calls for better readability.
  • DO wrap asynchronous calls in
    try-catch
    to handle errors gracefully.
  • PREFER
    Future.wait
    to initiate multiple independent futures concurrently.
  • PREFER
    await for
    over
    forEach
    or
    listen
    when consuming streams sequentially.
  • DO use
    StreamController
    with a
    close()
    call to prevent memory leaks.
  • DO NOT use
    await for
    for UI event listeners, as UI frameworks send endless streams of events which will block execution indefinitely.
  • Related Skills:
    dart-idiomatic-usage
    ,
    dart-concurrency-isolates
    .
  • 务必使用
    async
    /
    await
    而非原生
    .then()
    调用,以获得更好的可读性。
  • 务必将异步调用包裹在
    try-catch
    中,以优雅处理错误。
  • 优先使用
    Future.wait
    并发启动多个独立的Future。
  • 顺序消费流时,优先使用
    await for
    而非
    forEach
    listen
  • 务必为
    StreamController
    添加
    close()
    调用,以防止内存泄漏。
  • 请勿为UI事件监听器使用
    await for
    ,因为UI框架会发送无限的事件流,会无限期阻塞执行。
  • 相关技能:
    dart-idiomatic-usage
    dart-concurrency-isolates