Dart: Dart Asynchronous Programming — Future / async-await

Last updated: 2026-08-26

Asynchronous programming lets your program not wait — a 3-second I/O operation lets the CPU do other work instead of just waiting.

1. What You'll Learn


2. A Developer's Real Story

(1) Pain Point: Sequential Requests Cause 12-Second Page Load

Bob's DataPipeline needs to fetch data from 3 APIs: orders (3 seconds), products (2 seconds), and customers (2 seconds). He initially used sequential requests: 3 + 2 + 2 = 7 seconds. Adding the database query (5 seconds), the entire report generation took 12 seconds. The user experience was terrible, and SaaS customers complained it was "too slow."

(2) Solution: Concurrent Execution with Future.wait

Using Future.wait makes the 3 API requests run concurrently, with the total time determined by the slowest one: 3 seconds. Combined with concurrent database queries, the entire report generation is shortened to 4 seconds.

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 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

(3) Benefits


3. Future Basics

(1) Future Lifecycle

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 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

▶ Example

TEXT 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: Creating and Using a 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 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
Future State Meaning
pending Incomplete, waiting
completed with value Completed successfully, carrying a value
completed with error Completed with failure, carrying an exception

4. async / await In-Depth

(1) Basic Syntax

▶ Example

TEXT 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: async/await Basics

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 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

(2) Error Handling

▶ Example

TEXT 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: async/await Error Handling

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 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
Error Handling Method Syntax Use Case
try-catch try { await f(); } catch (e) {} When recovery is needed
catchError f().catchError((e) => ...) Functional style
onError f().then(..., onError: ...) Simple callbacks

5. Future Composition

(1) Future.wait — Concurrent Waiting

▶ Example

TEXT 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: Future.wait Concurrent

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 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

▶ Example

TEXT 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: Future.wait Error Handling

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 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

(2) Future.any — Racing

▶ Example

TEXT 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: Future.any Racing

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 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

(3) Future.forEach — Sequential Iteration

▶ Example

TEXT 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: 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 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.
Composition Method Behavior Concurrency Use Case
Future.wait Wait for all Concurrent Batch requests
Future.any First to complete Concurrent Racing/Fallback
Future.forEach Sequential completion Sequential Order-dependent operations

6. Completer Manual Control

▶ Example

TEXT 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: 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 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

7. Bob's Scenario: Concurrent API Requests

▶ Example

TEXT 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

: DataPipeline Concurrent Data Fetching

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 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

8. Complete Example: DataPipeline Asynchronous Data Processing

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 📖 Display only
> **Output:** Execute in DartPad or using `dart run`. All Dart course examples are based on Dart 3.x / Flutter 3.x. Results may vary slightly depending on the SDK version.

Output:

TEXT 📖 Display only
=== 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

❓ FAQ

Q: What is the return type of an async function? A: An async function automatically wraps the return value into a Future. Declare a Future<T> return type, and simply return T inside the function.

Q: Can await only be used in async functions? A: Yes. await can only be used in async functions or async* generators. Dart 3.5+ supports top-level await (only in Dart scripts).

Q: What happens if a Future fails in Future.wait? A: By default, if any Future fails, Future.wait immediately throws that exception (other Futures continue running but their results are ignored). You can use eagerError: false to wait for all to complete.

Q: What is the difference between async/await and .then()? A: They are functionally equivalent, but async/await is more readable (linear flow), while .then() is more functional (chaining). For daily use, async/await is recommended; .then() is suitable for simple transformations.

Q: When should I use Completer? A: Use Completer when you need to manually control the completion timing of a Future. Common scenarios: converting event callbacks to Futures, WebSocket messages, third-party library callbacks. In most cases, you can just use async/await.

Q: Is Future multicast or unicast? A: A Future is unicast — it can only be consumed by one await or .then(). For multicast, you need a Stream or asStream().

Q: How to implement timeout control? A: Use the Future.timeout() method. For example: await fetchApi().timeout(Duration(seconds: 5), onTimeout: () => defaultData).


📖 Summary


📝 Exercises

  1. Basic (⭐ Difficulty): Write 3 functions that return Futures (each with a delay of 1/2/3 seconds). Call them sequentially using async/await and measure the total time. Then call them concurrently using Future.wait and compare the times.
  2. Intermediate (⭐⭐ Difficulty): Implement an asynchronous request function with retry: fetchWithRetry(url, retries: 3, backoff: Duration). It should retry on failure with exponential backoff and throw an exception after exceeding the retry limit.
  3. Challenge (⭐⭐⭐ Difficulty): Implement a simple "request-response" protocol using Completer: send a request and wait for the corresponding response, with timeout support. Simulate a message bus where multiple requests can be sent concurrently.

← Previous Lesson | Next Lesson →

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

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

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