一、异步编程为什么让人又爱又恨

Dart语言中的异步编程就像外卖小哥送餐,你点了单(发起请求)之后不用干等着,可以继续刷剧(执行其他代码)。等餐到了(响应返回)自然会通知你。这种机制让程序不会卡死,但处理不好也容易翻车。

举个例子,我们经常用Future处理网络请求:

// 示例1:基本的Future使用(技术栈:Dart/Flutter)
Future<void> fetchUserData() async {
  print('开始获取用户数据...'); // 同步代码立即执行
  
  // 模拟网络请求延迟
  final data = await Future.delayed(
    Duration(seconds: 2),
    () => {'name': '老王', 'age': 30},
  );
  
  print('获取到数据: $data'); // 异步代码后执行
}

void main() {
  fetchUserData();
  print('我正在做其他事情...'); // 不会等待网络请求
}

这个简单的例子展示了异步的核心优势:非阻塞。但现实往往更复杂,比如多个异步任务嵌套时:

// 示例2:嵌套的异步陷阱(技术栈:Dart)
Future<void> buyCoffee() async {
  await login();          // 等待登录
  await selectCoffee();   // 等待选择
  await pay();            // 等待支付
  // 这里已经形成了"回调地狱"的雏形
}

二、那些年我们踩过的异步坑

1. 未处理的异常就像定时炸弹

// 示例3:未捕获的异步异常(技术栈:Dart)
Future<void> riskyOperation() async {
  await Future.delayed(Duration(seconds: 1));
  throw Exception('意外爆炸!');
}

void main() {
  riskyOperation(); // 没有处理异常
  print('程序继续运行...'); // 异常会被静默吞掉
}

正确的处理方式应该是:

// 示例4:正确的异常处理(技术栈:Dart)
void main() async {
  try {
    await riskyOperation();
  } catch (e) {
    print('捕获到异常: $e');
  }
}

2. 并行变串行的性能陷阱

// 示例5:错误的串行执行(技术栈:Dart)
Future<void> fetchAllData() async {
  final user = await fetchUser();   // 等待用户数据
  final posts = await fetchPosts(); // 然后才请求帖子
  // 两个本可以并行的请求变成了串行
}

应该使用Future.wait实现并行:

// 示例6:正确的并行处理(技术栈:Dart)
Future<void> fetchAllData() async {
  final results = await Future.wait([
    fetchUser(),
    fetchPosts(),
  ]);
  // 同时发起两个请求
}

三、Stream处理的常见误区

Dart中的Stream就像传送带,数据会源源不断过来。新手常犯的错误是忘记关闭:

// 示例7:Stream泄漏问题(技术栈:Dart)
Stream<int> countNumbers() async* {
  var i = 0;
  while (true) {
    yield i++;
    await Future.delayed(Duration(seconds: 1));
  }
}

void main() {
  final subscription = countNumbers().listen((num) {
    print(num);
    if (num == 5) {
      // 忘记调用 subscription.cancel();
    }
  });
  // Stream会持续占用资源
}

正确的做法是:

// 示例8:正确的Stream处理(技术栈:Dart)
void main() async {
  final subscription = countNumbers().listen((num) {
    print(num);
    if (num == 5) {
      subscription.cancel(); // 及时取消订阅
    }
  });
}

四、实战中的最佳实践

1. 超时机制必不可少

// 示例9:添加超时控制(技术栈:Dart)
Future<void> fetchWithTimeout() async {
  try {
    final data = await fetchUserData()
      .timeout(Duration(seconds: 3));
    print(data);
  } on TimeoutException {
    print('请求超时');
  }
}

2. 使用async/await替代then

// 示例10:两种风格对比(技术栈:Dart)
// 旧式then写法
fetchUser().then((user) {
  return fetchPosts(user.id);
}).then((posts) {
  print(posts);
});

// 更清晰的async/await写法
void main() async {
  final user = await fetchUser();
  final posts = await fetchPosts(user.id);
  print(posts);
}

3. 重要数据需要错误重试

// 示例11:带重试机制的请求(技术栈:Dart)
Future<T> retry<T>(Future<T> Function() fn, 
                  {int maxAttempts = 3}) async {
  for (var i = 0; i < maxAttempts; i++) {
    try {
      return await fn();
    } catch (e) {
      if (i == maxAttempts - 1) rethrow;
      await Future.delayed(Duration(seconds: 1));
    }
  }
  throw StateError('不可能执行到这里');
}

五、Isolate不是银弹

虽然Isolate可以实现真正的并行,但通信成本很高:

// 示例12:Isolate的通信开销(技术栈:Dart)
void isolateFunction(SendPort sendPort) {
  var counter = 0;
  Timer.periodic(Duration(seconds: 1), (_) {
    sendPort.send(counter++); // 每次通信都有序列化开销
  });
}

void main() async {
  final receivePort = ReceivePort();
  await Isolate.spawn(isolateFunction, receivePort.sendPort);
  
  receivePort.listen((message) {
    print('收到: $message');
  });
}

六、总结与建议

  1. 始终处理异步异常,不要静默失败
  2. 能用async/await就别用then回调
  3. 并行任务优先考虑Future.wait
  4. Stream使用后记得取消订阅
  5. 关键操作添加超时和重试机制
  6. Isolate适合CPU密集型任务,但要注意通信成本

记住,异步编程就像烹饪,火候(时机)掌握不好,再好的食材(代码)也会煮糊。多练习这些模式,你就能像大厨一样游刃有余。