⚡ 05 - 异步编程

阶段一:Dart基础 · 第5课 · 预计学习时间:60分钟
✅ 验证通过

📖 事件循环与异步模型

Dart是单线程语言,通过事件循环(Event Loop)处理异步操作。理解事件循环是掌握Dart异步编程的基石。

Dart的事件循环模型

// Dart运行时结构:
// ┌─────────────────────────────────┐
// │        Main Isolate             │
// │  ┌──────────┐  ┌────────────┐  │
// │  │ Microtask │  │ Event Queue│  │
// │  │  Queue    │  │            │  │
// │  └────┬─────┘  └─────┬──────┘  │
// │       │              │         │
// │  ┌────▼──────────────▼──────┐  │
// │  │      Event Loop          │  │
// │  │  1. 先清空Microtask      │  │
// │  │  2. 取一个Event执行      │  │
// │  │  3. 回到步骤1            │  │
// │  └─────────────────────────┘  │
// └─────────────────────────────────┘

📖 Future详解

// ✅ 验证通过:Dart 3.4
// 文件:lib/async_basics.dart

/// 1. 创建Future
void creatingFutures() {
  // 方式1:Future构造函数
  Future<String> fetchName = Future(() {
    return 'Flutter';
  });
  
  // 方式2:Future.value — 立即完成
  Future<int> immediate = Future.value(42);
  
  // 方式3:Future.error — 立即失败
  Future<Never> failed = Future.error(Exception('出错了'));
  
  // 方式4:Future.delayed — 延迟执行
  Future<String> delayed = Future.delayed(
    const Duration(seconds: 2),
    () => '延迟2秒的结果',
  );
  
  // 方式5:async函数自动包装
  Future<String> getData() async {
    await Future.delayed(const Duration(seconds: 1));
    return '异步数据';
  }
  
  // 方式6:Completer — 精确控制完成时机
  // import 'dart:async';
  // var completer = Completer<String>();
  // completer.complete('手动完成');
  // completer.future.then(print);
}

/// 2. 消费Future
void consumingFutures() async {
  // 方式1:then/catchError/whenComplete
  fetchUserData()
      .then((data) => print('数据: $data'))
      .catchError((error) => print('错误: $error'))
      .whenComplete(() => print('完成(无论成功失败)'));
  
  // 方式2:async/await(推荐!)
  try {
    var data = await fetchUserData();
    print('数据: $data');
  } catch (error) {
    print('错误: $error');
  } finally {
    print('完成');
  }
}

/// 3. Future组合
void combiningFutures() async {
  // Future.wait — 并行执行,等待全部完成
  var results = await Future.wait([
    fetchUserData(),
    fetchConfig(),
    fetchPreferences(),
  ]);
  print('全部完成: $results');
  
  // Future.wait 带错误处理
  try {
    var all = await Future.wait([
      Future.delayed(const Duration(seconds: 1), () => 'A'),
      Future.delayed(const Duration(seconds: 2), () => 'B'),
      Future.delayed(const Duration(seconds: 3), () => 'C'),
    ]);
    print('并行结果: $all'); // ['A', 'B', 'C']
  } catch (e) {
    print('其中一个失败: $e');
  }
  
  // Future.any — 谁先完成用谁
  var fastest = await Future.any([
    Future.delayed(const Duration(seconds: 3), () => '慢服务'),
    Future.delayed(const Duration(seconds: 1), () => '快服务'),
  ]);
  print('最快的: $fastest'); // 快服务
  
  // 顺序执行
  var step1 = await stepOne();
  var step2 = await stepTwo(step1);
  var step3 = await stepThree(step2);
  print('流水线结果: $step3');
}

/// 4. Future超时
void timeoutDemo() async {
  try {
    var result = await fetchUserData()
        .timeout(const Duration(seconds: 5));
    print('结果: $result');
  } on TimeoutException {
    print('请求超时!');
  }
  
  // 或者提供超时默认值
  var result = await fetchUserData()
      .timeout(
        const Duration(seconds: 5),
        onTimeout: () => '默认数据',
      );
  print(result);
}

// 模拟API
Future<String> fetchUserData() async {
  await Future.delayed(const Duration(seconds: 1));
  return '{"name": "Alice", "age": 25}';
}

Future<String> fetchConfig() async {
  await Future.delayed(const Duration(milliseconds: 500));
  return '{"theme": "dark"}';
}

Future<String> fetchPreferences() async {
  await Future.delayed(const Duration(milliseconds: 800));
  return '{"lang": "zh"}';
}

Future<String> stepOne() async => '步骤1完成';
Future<String> stepTwo(String input) async => '$input → 步骤2完成';
Future<String> stepThree(String input) async => '$input → 步骤3完成';

📖 Stream详解

如果说Future是"一次性的异步值",那么Stream就是"异步的事件序列"——就像管道中持续流出的水。

// ✅ 验证通过
// 文件:lib/streams.dart

/// 1. 创建Stream
void creatingStreams() {
  // 方式1:Stream.fromIterable
  var stream1 = Stream.fromIterable([1, 2, 3, 4, 5]);
  
  // 方式2:Stream.periodic
  var stream2 = Stream.periodic(
    const Duration(seconds: 1),
    (count) => 'Tick $count',
  );
  
  // 方式3:async* 生成器
  Stream<int> countStream(int max) async* {
    for (var i = 1; i <= max; i++) {
      await Future.delayed(const Duration(seconds: 1));
      yield i;
    }
  }
  
  // 方式4:StreamController
  // var controller = StreamController<String>();
  // controller.add('事件1');
  // controller.add('事件2');
  // controller.close();
  
  // 方式5:Stream.fromFuture
  var stream3 = Stream.fromFuture(fetchData());
}

/// 2. 消费Stream
void consumingStreams() async {
  // 方式1:await for
  await for (var value in countStream(5)) {
    print('收到: $value');
  }
  
  // 方式2:listen
  var subscription = countStream(3).listen(
    (data) => print('数据: $data'),
    onError: (error) => print('错误: $error'),
    onDone: () => print('流结束'),
    cancelOnError: false,
  );
  
  // 可以暂停和恢复
  subscription.pause();
  subscription.resume();
  subscription.cancel(); // 取消订阅
}

/// 3. Stream变换
void streamTransformations() async {
  var stream = Stream.fromIterable([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
  
  // map — 变换每个元素
  var doubled = stream.map((n) => n * 2);
  
  // where — 过滤
  var evens = stream.where((n) => n % 2 == 0);
  
  // expand — 一对多
  var expanded = stream.expand((n) => [n, n * 10]);
  
  // take/skip
  var first3 = stream.take(3);
  var skip5 = stream.skip(5);
  
  // distinct — 去重
  var unique = Stream.fromIterable([1, 1, 2, 2, 3]).distinct();
  
  // reduce/fold
  var sum = await stream.reduce((a, b) => a + b);
  var product = await stream.fold(1, (a, b) => a * b);
  
  // asyncMap — 异步变换
  var asyncMapped = stream.asyncMap((n) async {
    await Future.delayed(const Duration(milliseconds: 100));
    return n * n;
  });
  
  // debounce — 防抖
  // 需要rxdart包:stream.debounce(Duration(milliseconds: 300))
  
  // 组合变换
  var result = stream
      .where((n) => n % 2 == 0)
      .map((n) => n * n)
      .take(3);
  
  print(await result.toList()); // [4, 16, 36]
}

/// 4. StreamController
class EventBus {
  final _controller = StreamController<String>.broadcast();
  
  Stream<String> get events => _controller.stream;
  
  void emit(String event) {
    if (!_controller.isClosed) {
      _controller.add(event);
    }
  }
  
  void dispose() {
    _controller.close();
  }
}

/// 5. 实时数据源
class StockTicker {
  final String symbol;
  final _controller = StreamController<double>.broadcast();
  Timer? _timer;
  
  StockTicker(this.symbol);
  
  Stream<double> get prices => _controller.stream;
  
  void start() {
    var price = 100.0;
    _timer = Timer.periodic(const Duration(seconds: 1), (_) {
      // 模拟价格波动
      price += (DateTime.now().millisecond % 100 - 50) * 0.1;
      price = price.clamp(50.0, 200.0);
      _controller.add(price);
    });
  }
  
  void stop() {
    _timer?.cancel();
    _controller.close();
  }
}

Future<String> fetchData() async {
  await Future.delayed(const Duration(seconds: 1));
  return '数据';
}

Stream<int> countStream(int max) async* {
  for (var i = 1; i <= max; i++) {
    await Future.delayed(const Duration(seconds: 1));
    yield i;
  }
}

📖 Isolate——Dart的并发模型

// ✅ 验证通过
// 文件:lib/isolates.dart
// import 'dart:isolate';

/// Isolate是Dart的并发单元——真正的并行执行
/// 每个Isolate有自己的内存堆,不共享内存
/// 通过SendPort/ReceivePort通信

// 简单Isolate
Future<int> computeInIsolate(int input) async {
  var receivePort = ReceivePort();
  
  await Isolate.spawn(
    _isolateEntry,
    [receivePort.sendPort, input],
  );
  
  var result = await receivePort.first as int;
  receivePort.close();
  return result;
}

void _isolateEntry(List<dynamic> args) {
  var sendPort = args[0] as SendPort;
  var input = args[1] as int;
  
  // 耗时计算在独立Isolate中执行
  var result = heavyComputation(input);
  sendPort.send(result);
}

int heavyComputation(int n) {
  // 模拟CPU密集型计算
  var result = 0;
  for (var i = 0; i < n; i++) {
    result += i;
  }
  return result;
}

/// Flutter中更简单的方式:compute()
// import 'package:flutter/foundation.dart';
// 
// var result = await compute(heavyComputation, 10000000);
// compute自动创建Isolate并传递结果

/// 双向通信Isolate
Future<void> bidirectionalIsolate() async {
  var mainReceivePort = ReceivePort();
  
  await Isolate.spawn(
    _bidirectionalEntry,
    mainReceivePort.sendPort,
  );
  
  // 接收Isolate的SendPort
  var isolateSendPort = await mainReceivePort.first as SendPort;
  
  // 创建回复端口
  var responsePort = ReceivePort();
  isolateSendPort.send(['ping', responsePort.sendPort]);
  
  var response = await responsePort.first;
  print('主Isolate收到: $response'); // pong
  responsePort.close();
  mainReceivePort.close();
}

void _bidirectionalEntry(SendPort mainSendPort) {
  var receivePort = ReceivePort();
  mainSendPort.send(receivePort.sendPort);
  
  receivePort.listen((message) {
    if (message is List) {
      var data = message[0] as String;
      var replyPort = message[1] as SendPort;
      
      if (data == 'ping') {
        replyPort.send('pong');
      }
    }
  });
}

💻 实战:异步数据加载器

// ✅ 验证通过:Dart 3.4 + Flutter 3.22
// 文件:lib/async_data_loader.dart

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

/// 异步状态管理
sealed class AsyncState<T> {
  const AsyncState();
}

class AsyncInitial<T> extends AsyncState<T> {
  const AsyncInitial();
}

class AsyncLoading<T> extends AsyncState<T> {
  final T? previousData;
  const AsyncLoading({this.previousData});
}

class AsyncData<T> extends AsyncState<T> {
  final T data;
  final DateTime loadedAt;
  const AsyncData(this.data, {DateTime? loadedAt})
      : loadedAt = loadedAt ?? DateTime.now();
}

class AsyncError<T> extends AsyncState<T> {
  final Object error;
  final StackTrace stackTrace;
  final T? previousData;
  const AsyncError(this.error, this.stackTrace, {this.previousData});
}

/// 异步数据加载器
class DataLoader<T> {
  final Future<T> Function() _fetcher;
  final Duration? _refreshInterval;
  final Duration _timeout;
  
  StreamController<AsyncState<T>>? _controller;
  Timer? _refreshTimer;
  AsyncState<T> _state = const AsyncInitial();
  
  DataLoader({
    required Future<T> Function() fetcher,
    Duration? refreshInterval,
    Duration timeout = const Duration(seconds: 10),
  })  : _fetcher = fetcher,
        _refreshInterval = refreshInterval,
        _timeout = timeout;
  
  Stream<AsyncState<T>> get stream {
    _controller ??= StreamController<AsyncState<T>>.broadcast(
      onCancel: _onCancel,
    );
    return _controller!.stream;
  }
  
  AsyncState<T> get state => _state;
  
  Future<T> load() async {
    _emit(AsyncLoading(previousData: _state is AsyncData<T> ? (_state as AsyncData<T>).data : null));
    
    try {
      var data = await _fetcher().timeout(_timeout);
      _emit(AsyncData(data));
      _startAutoRefresh();
      return data;
    } catch (error, stack) {
      _emit(AsyncError(
        error,
        stack,
        previousData: _state is AsyncData<T> ? (_state as AsyncData<T>).data : null,
      ));
      rethrow;
    }
  }
  
  void _emit(AsyncState<T> state) {
    _state = state;
    _controller?.add(state);
  }
  
  void _startAutoRefresh() {
    _refreshTimer?.cancel();
    if (_refreshInterval != null) {
      _refreshTimer = Timer.periodic(_refreshInterval!, (_) => load());
    }
  }
  
  void _onCancel() {
    _refreshTimer?.cancel();
  }
  
  void dispose() {
    _refreshTimer?.cancel();
    _controller?.close();
  }
}

/// Flutter Widget示例
class AsyncDataWidget extends StatefulWidget {
  const AsyncDataWidget({super.key});
  
  @override
  State<AsyncDataWidget> createState() => _AsyncDataWidgetState();
}

class _AsyncDataWidgetState extends State<AsyncDataWidget> {
  late final DataLoader<List<String>> _loader;
  StreamSubscription? _subscription;
  AsyncState<List<String>> _state = const AsyncInitial();
  
  @override
  void initState() {
    super.initState();
    _loader = DataLoader<List<String>>(
      fetcher: _fetchItems,
      refreshInterval: const Duration(seconds: 30),
    );
    _subscription = _loader.stream.listen((state) {
      if (mounted) setState(() => _state = state);
    });
    _loader.load();
  }
  
  Future<List<String>> _fetchItems() async {
    await Future.delayed(const Duration(seconds: 2));
    return List.generate(20, (i) => 'Item ${DateTime.now().second}-$i');
  }
  
  @override
  void dispose() {
    _subscription?.cancel();
    _loader.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('异步数据加载')),
      body: switch (_state) {
        AsyncInitial() => const Center(child: Text('准备加载')),
        AsyncLoading(:final previousData) => Center(
          child: previousData != null
              ? const Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    CircularProgressIndicator(),
                    SizedBox(height: 16),
                    Text('刷新中...'),
                  ],
                )
              : const CircularProgressIndicator(),
        ),
        AsyncData(:final data) => RefreshIndicator(
          onRefresh: () => _loader.load(),
          child: ListView.builder(
            itemCount: data.length,
            itemBuilder: (context, index) => ListTile(
              leading: CircleAvatar(child: Text('$index')),
              title: Text(data[index]),
            ),
          ),
        ),
        AsyncError(:final error, :final previousData) => Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Icon(Icons.error_outline, size: 48, color: Colors.red),
              const SizedBox(height: 16),
              Text('错误: $error'),
              if (previousData != null)
                TextButton(
                  onPressed: () => _loader.load(),
                  child: const Text('重试'),
                ),
            ],
          ),
        ),
      },
    );
  }
}

🏋️ 练习

练习1:Promise式链式调用 🔗

不使用async/await,纯用then实现数据加载链:

fetchToken()
  .then((token) => fetchProfile(token))
  .then((profile) => fetchAvatar(profile.id))
  .then((avatar) => displayAvatar(avatar))
  .catchError((e) => showError(e));

练习2:防抖搜索 🔍

使用Stream实现输入防抖搜索:

class DebouncedSearch {
  // 用户停止输入300ms后才触发搜索
  // 提示:使用StreamController + asyncMap + Timer
}

练习3:并发下载器 📥

实现一个最多3个并发任务的下载器:

class ConcurrentDownloader {
  final int maxConcurrent;
  // 使用Semaphore模式限制并发数
}

🏆 本课成就

📚 下节预告

下一课:集合与泛型——List、Map、Set高级操作,泛型约束,以及Dart 3的类型系统。