Dart: Dart 异步编程 — Future / async-await 详解

异步让程序不等待 — 一个 I/O 操作 3 秒,异步让 CPU 去干别的,而不是傻等。

1. 你将学到


2. 一个开发者的真实故事

(1) 痛点:顺序请求导致页面加载 12 秒

Bob 的 DataPipeline 需要从 3 个 API 获取数据:订单(3 秒)、产品(2 秒)、客户(2 秒)。他最初用顺序请求:3 + 2 + 2 = 7 秒。加上数据库查询(5 秒),整个报表生成需要 12 秒。用户体验极差,SaaS 客户抱怨"太慢了"。

(2) Future.wait 并发的解法

用 Future.wait 让 3 个 API 请求并发执行,总时间取决于最慢的那个:3 秒。加上并发数据库查询,整个报表生成缩短到 4 秒。

DART
// Sequential: 3 + 2 + 2 = 7 seconds
final orders = await fetchOrders();     // 3s
final products = await fetchProducts(); // 2s
final customers = await fetchCustomers(); // 2s

// Concurrent: max(3, 2, 2) = 3 seconds
final results = await Future.wait([
  fetchOrders(),
  fetchProducts(),
  fetchCustomers(),
]);
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(3) 收益


3. Future 基础

(1) Future 生命周期

100%
sequenceDiagram
  participant Bob
  participant API1
  participant API2
  participant API3
  Bob->>API1: fetchOrders()
  Bob->>API2: fetchProducts()
  Bob->>API3: fetchCustomers()
  Note over Bob: Future.wait concurrent
  API1-->>Bob: 1.2M orders
  API2-->>Bob: 500K products
  API3-->>Bob: 300K customers
  Bob->>Bob: merge & analyze
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:创建与使用 Future

DART
import 'dart:async';

// Create a Future that completes after a delay
Future``<String>`` fetchOrder() async {
  await Future.delayed(const Duration(seconds: 2));
  return 'ORD-001: \$1500.00 USD';
}

// Future with error
Future``<String>`` fetchOrderWithRetry() {
  return Future.delayed(const Duration(seconds: 1), () {
    throw Exception('Network timeout');
  });
}

void main() async {
  // Pending → Completed
  print('Fetching order...');
  final order = await fetchOrder();
  print(order);

  // Pending → Failed
  try {
    await fetchOrderWithRetry();
  } catch (e) {
    print('Error: $e');
  }
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
Future 状态 含义
pending 未完成,等待中
completed with value 成功完成,携带值
completed with error 失败完成,携带异常

4. async / await 详解

(1) 基础语法

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:async/await 基础

DART
import 'dart:async';

Future``<double>`` calculateOrderTotal(String orderId) async {
  // await pauses execution until Future completes
  final amount = await fetchAmount(orderId);
  final taxRate = await fetchTaxRate(orderId);
  return amount * (1 + taxRate);
}

Future``<double>`` fetchAmount(String orderId) async {
  await Future.delayed(const Duration(milliseconds: 500));
  return 1500.0;
}

Future``<double>`` fetchTaxRate(String orderId) async {
  await Future.delayed(const Duration(milliseconds: 300));
  return 0.08;
}

void main() async {
  final total = await calculateOrderTotal('ORD-001');
  print('Total: \$${total.toStringAsFixed(2)} USD');
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(2) 错误处理

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:async/await 错误处理

DART
import 'dart:async';

Future``<String>`` fetchApiData(String endpoint) async {
  await Future.delayed(const Duration(seconds: 1));
  if (endpoint.contains('invalid')) {
    throw Exception('API error: $endpoint not found');
  }
  return 'Data from $endpoint';
}

Future``<void>`` robustFetch(String endpoint) async {
  try {
    final data = await fetchApiData(endpoint);
    print('Success: $data');
  } on Exception catch (e) {
    print('Exception: $e');
  } finally {
    print('Fetch completed for $endpoint');
  }
}

void main() async {
  await robustFetch('orders');           // Success
  await robustFetch('invalid-endpoint'); // Exception
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
错误处理方式 语法 适用场景
try-catch try { await f(); } catch (e) {} 需要恢复
catchError f().catchError((e) => ...) 函数式风格
onError f().then(..., onError: ...) 简单回调

5. Future 组合

(1) Future.wait — 并发等待

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:Future.wait 并发

DART
import 'dart:async';

Future<List``<String>``> fetchOrders() async {
  await Future.delayed(const Duration(seconds: 3));
  return ['ORD-001', 'ORD-002', 'ORD-003'];
}

Future<List``<String>``> fetchProducts() async {
  await Future.delayed(const Duration(seconds: 2));
  return ['Laptop', 'Mouse', 'Keyboard'];
}

Future<List``<String>``> fetchCustomers() async {
  await Future.delayed(const Duration(seconds: 2));
  return ['Alice', 'Bob', 'Charlie'];
}

void main() async {
  // Concurrent: max(3, 2, 2) = ~3 seconds
  final stopwatch = Stopwatch()..start();
  final results = await Future.wait([
    fetchOrders(),
    fetchProducts(),
    fetchCustomers(),
  ]);
  stopwatch.stop();

  print('Orders:   ${results[0]}');
  print('Products: ${results[1]}');
  print('Customers: ${results[2]}');
  print('Time: ${stopwatch.elapsedMilliseconds}ms');
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:Future.wait 错误处理

DART
import 'dart:async';

Future``<String>`` riskyFetch(String name, bool shouldFail) async {
  await Future.delayed(const Duration(seconds: 1));
  if (shouldFail) throw Exception('$name failed');
  return '$name data';
}

void main() async {
  // Future.wait fails fast - if any fails, the whole thing fails
  try {
    await Future.wait([
      riskyFetch('API-1', false),
      riskyFetch('API-2', true),   // This one fails
      riskyFetch('API-3', false),
    ]);
  } catch (e) {
    print('Future.wait failed: $e');
  }

  // Preserve individual results with eagerError: false
  final results = await Future.wait(
    [
      riskyFetch('API-1', false).then((v) => Result.success(v)),
      riskyFetch('API-2', true).then((v) => Result.success(v)).catchError((e) => Result.failure(e.toString())),
      riskyFetch('API-3', false).then((v) => Result.success(v)),
    ],
  );

  for (final r in results) {
    print(r.isSuccess ? 'OK: ${r.data}' : 'FAIL: ${r.error}');
  }
}

class Result``<T>`` {
  final T? data;
  final String? error;
  final bool isSuccess;
  Result.success(this.data) : error = null, isSuccess = true;
  Result.failure(this.error) : data = null, isSuccess = false;
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(2) Future.any — 竞速

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:Future.any 竞速

DART
import 'dart:async';

Future``<String>`` fetchFromCache() async {
  await Future.delayed(const Duration(milliseconds: 100));
  return 'Cache: ORD-001 data';
}

Future``<String>`` fetchFromApi() async {
  await Future.delayed(const Duration(seconds: 2));
  return 'API: ORD-001 data';
}

Future``<String>`` fetchFromDb() async {
  await Future.delayed(const Duration(milliseconds: 500));
  return 'DB: ORD-001 data';
}

void main() async {
  // Returns the FIRST to complete
  final fastest = await Future.any([
    fetchFromCache(),
    fetchFromApi(),
    fetchFromDb(),
  ]);
  print('Fastest: $fastest');  // Cache: ORD-001 data
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

(3) Future.forEach — 顺序迭代

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:Future.forEach

DART
import 'dart:async';

Future``<void>`` processOrder(String orderId) async {
  await Future.delayed(const Duration(milliseconds: 500));
  print('Processed: $orderId');
}

void main() async {
  final orders = ['ORD-001', 'ORD-002', 'ORD-003'];

  // Process sequentially
  await Future.forEach(orders, processOrder);
  print('All orders processed');
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。
组合方法 行为 并发 适用场景
Future.wait 全部完成 并发 批量请求
Future.any 第一个完成 并发 竞速/降级
Future.forEach 顺序完成 顺序 依赖顺序的操作

6. Completer 手动控制

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:Completer

DART
import 'dart:async';

// Manual Future control with Completer
class ApiService {
  final Completer``<String>`` _initCompleter = Completer``<String>``();

  Future``<String>`` get initialized => _initCompleter.future;

  void onConnected(String serverInfo) {
    if (!_initCompleter.isCompleted) {
      _initCompleter.complete(serverInfo);
    }
  }

  void onError(Object error) {
    if (!_initCompleter.isCompleted) {
      _initCompleter.completeError(error);
    }
  }
}

void main() async {
  final service = ApiService();

  // Simulate async initialization
  Future.delayed(const Duration(seconds: 1), () {
    service.onConnected('Server v3.0.1, 1,000,000 records');
  });

  print('Waiting for initialization...');
  final info = await service.initialized;
  print('Connected: $info');
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

7. Bob 场景:并发 API 请求

▶ 示例

TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

:DataPipeline 并发数据获取

DART
import 'dart:async';

// Simulated API calls
Future<List<Map<String, dynamic>>> fetchOrders() async {
  await Future.delayed(const Duration(seconds: 2));
  return [
    {'id': 'ORD-001', 'amount': 1500.0, 'category': 'Electronics'},
    {'id': 'ORD-002', 'amount': 3200.0, 'category': 'Electronics'},
    {'id': 'ORD-003', 'amount': 890.0, 'category': 'Clothing'},
  ];
}

Future<Map<String, double>> fetchProductPrices() async {
  await Future.delayed(const Duration(seconds: 1));
  return {'Laptop': 1299.99, 'Mouse': 29.99, 'Keyboard': 79.99};
}

Future<List``<String>``> fetchCustomerNames() async {
  await Future.delayed(const Duration(seconds: 1));
  return ['Alice', 'Bob', 'Charlie'];
}

// Concurrent data fetching with error handling
Future``<void>`` generateReport() async {
  final stopwatch = Stopwatch()..start();

  try {
    // Fetch all data concurrently
    final results = await Future.wait([
      fetchOrders().then((v) => ('orders', v)).catchError((e) => ('orders', null)),
      fetchProductPrices().then((v) => ('products', v)).catchError((e) => ('products', null)),
      fetchCustomerNames().then((v) => ('customers', v)).catchError((e) => ('customers', null)),
    ]);

    stopwatch.stop();

    print('=== DataPipeline Report ===');
    print('Fetch time: ${stopwatch.elapsedMilliseconds}ms');

    for (final (key, value) in results) {
      if (value != null) {
        print('$key: OK (${value is List ? value.length : value.length} items)');
      } else {
        print('$key: FAILED');
      }
    }
  } catch (e) {
    print('Report generation failed: $e');
  }
}

void main() async {
  await generateReport();
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

8. 完整示例:DataPipeline 异步数据处理

DART
// ============================================
// DataPipeline Async Data Processing
// Future, async/await, and concurrent operations
// ============================================

import 'dart:async';

// Simulated data sources
Future<List<Map<String, dynamic>>> fetchOrders({int delay = 2}) async {
  await Future.delayed(Duration(seconds: delay));
  return [
    {'id': 'ORD-001', 'amount': 1500.0, 'status': 'completed', 'category': 'Electronics'},
    {'id': 'ORD-002', 'amount': 50.0, 'status': 'completed', 'category': 'Books'},
    {'id': 'ORD-003', 'amount': 3200.0, 'status': 'pending', 'category': 'Electronics'},
    {'id': 'ORD-004', 'amount': 890.0, 'status': 'completed', 'category': 'Clothing'},
  ];
}

Future<List<Map<String, dynamic>>> fetchProducts({int delay = 1}) async {
  await Future.delayed(Duration(seconds: delay));
  return [
    {'name': 'Laptop', 'price': 1299.99, 'category': 'Electronics'},
    {'name': 'Mouse', 'price': 29.99, 'category': 'Electronics'},
    {'name': 'Novel', 'price': 12.99, 'category': 'Books'},
  ];
}

Future``<double>`` fetchTaxRate(String region) async {
  await Future.delayed(const Duration(milliseconds: 500));
  return switch (region) {
    'US' => 0.08,
    'EU' => 0.20,
    'UK' => 0.15,
    _ => 0.10,
  };
}

// Async pipeline with concurrent operations
class AsyncPipeline {
  final String name;
  String status = 'idle';

  AsyncPipeline({required this.name});

  Future<Map<String, dynamic>> run({String region = 'US'}) async {
    status = 'running';
    final stopwatch = Stopwatch()..start();

    try {
      // Step 1: Concurrent data fetch
      final (orders, products, taxRate) = await (
        fetchOrders(),
        fetchProducts(),
        fetchTaxRate(region),
      ).wait;

      // Step 2: Process orders
      final completedOrders = orders
          .where((o) => o['status'] == 'completed')
          .toList();

      // Step 3: Calculate revenue
      final revenue = completedOrders.fold``<double>``(
        0, (sum, o) => sum + (o['amount'] as double));

      final taxAmount = revenue * taxRate;
      final totalWithTax = revenue + taxAmount;

      // Step 4: Group by category
      final byCategory = <String, double>{};
      for (final order in completedOrders) {
        final cat = order['category'] as String;
        final amt = order['amount'] as double;
        byCategory.update(cat, (v) => v + amt, ifAbsent: () => amt);
      }

      stopwatch.stop();
      status = 'completed';

      return {
        'pipeline': name,
        'region': region,
        'taxRate': taxRate,
        'totalOrders': orders.length,
        'completedOrders': completedOrders.length,
        'revenue': revenue,
        'tax': taxAmount,
        'totalWithTax': totalWithTax,
        'byCategory': byCategory,
        'processingTime': stopwatch.elapsedMilliseconds,
        'status': status,
      };
    } catch (e) {
      status = 'failed';
      rethrow;
    }
  }
}

void main() async {
  final pipeline = AsyncPipeline(name: 'E-Commerce Analytics');

  print('=== Starting DataPipeline ===');
  final report = await pipeline.run(region: 'US');

  print('\n=== Report ===');
  print('Pipeline:    ${report['pipeline']}');
  print('Region:      ${report['region']}');
  print('Tax Rate:    ${(report['taxRate'] as double * 100).toStringAsFixed(1)}%');
  print('Total Orders: ${report['totalOrders']}');
  print('Completed:   ${report['completedOrders']}');
  print('Revenue:     \$${(report['revenue'] as double).toStringAsFixed(2)} USD');
  print('Tax:         \$${(report['tax'] as double).toStringAsFixed(2)} USD');
  print('Total:       \$${(report['totalWithTax'] as double).toStringAsFixed(2)} USD');
  print('Time:        ${report['processingTime']}ms');

  print('\nBy Category:');
  for (final entry in (report['byCategory'] as Map<String, double>).entries) {
    print('  ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
  }
}
TEXT 📖 仅展示
> **输出:** 在本地 DartPad 或 `dart run` 执行。Dart 课程所有示例基于 Dart 3.x / Flutter 3.x,运行结果会因 SDK 版本略有差异。

输出:

TEXT 📖 仅展示
=== Starting DataPipeline ===

=== Report ===
Pipeline:    E-Commerce Analytics
Region:      US
Tax Rate:    8.0%
Total Orders: 4
Completed:   3
Revenue:     $2440.00 USD
Tax:         $195.20 USD
Total:       $2635.20 USD
Time:        2000ms

By Category:
  Electronics: $1500.00 USD
  Books: $50.00 USD
  Clothing: $890.00 USD

❓ 常见问题

Q:async 函数的返回值类型是什么? A:async 函数自动将返回值包装为 Future。声明 `Future````` 返回类型,函数内直接 return T 即可。

Q:await 只能在 async 函数中使用吗? A:是的。await 只能在 async 函数或 async 生成器中使用。Dart 3.5+ 支持顶层 await(仅在 Dart 脚本中)。*

Q:Future.wait 中某个 Future 失败会怎样? A:默认情况下,任何一个 Future 失败,Future.wait 立即抛出该异常(其他 Future 继续运行但结果被忽略)。可用 eagerError: false 等待所有完成。

Q:async/await 和 .then() 有什么区别? A:功能等价,但 async/await 更可读(线性流程),.then() 更函数式(链式调用)。推荐日常用 async/await,简单转换用 .then()。

Q:Completer 什么时候用? A:当你需要手动控制 Future 的完成时机时用 Completer。常见场景:事件回调转 Future、WebSocket 消息、第三方库回调。大多数场景直接用 async/await 即可。

Q:Future 是多播还是单播? A:Future 是单播的 — 只能被一个 await 或 .then() 消费。多播需要用 Stream 或 asStream()。

Q:如何实现超时控制? A:用 Future.timeout() 方法。如 await fetchApi().timeout(Duration(seconds: 5), onTimeout: () => defaultData)


📖 小节


📝 作业

  1. 基础题(难度⭐):写 3 个返回 Future 的函数(分别延迟 1/2/3 秒),用 async/await 顺序调用,测量总时间;再用 Future.wait 并发调用,对比时间。
  2. 进阶题(难度⭐⭐):实现一个带重试的异步请求函数 fetchWithRetry(url, retries: 3, backoff: Duration),失败时按指数退避重试,超过次数后抛出异常。
  3. 挑战题(难度⭐⭐⭐):用 Completer 实现一个简单的"请求-响应"协议:发送请求后等待对应的响应,支持超时控制。模拟一个消息总线,多个请求可以并发发送。

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏