Dart Lesson 44 – Async Patterns | Dataplexa

Dart Asynchronous Patterns

In this lesson, you will learn asynchronous design patterns in Dart. These patterns help you write clean, scalable, and maintainable async code.

As applications grow, simple async/await is not always enough. Asynchronous patterns provide structure for handling complex workflows.


Why Asynchronous Patterns Matter

Without proper patterns, async code can become:

  • Difficult to read
  • Hard to debug
  • Error-prone
  • Unscalable

Patterns solve these issues by defining clear execution flows.


Pattern 1: Sequential Async Execution

Tasks are executed one after another. Each task waits for the previous one to finish.

This pattern is useful when tasks depend on earlier results.

Future fetchUserId() async {
  await Future.delayed(Duration(seconds: 1));
  return 101;
}

Future fetchUserName(int id) async {
  await Future.delayed(Duration(seconds: 1));
  return "User_$id";
}

void main() async {
  int id = await fetchUserId();
  String name = await fetchUserName(id);
  print(name);
}

Use this when execution order matters.


Pattern 2: Parallel Execution

Multiple independent tasks can run concurrently. This improves performance.

Future fetchOrders() async {
  await Future.delayed(Duration(seconds: 2));
  return 12;
}

Future fetchRevenue() async {
  await Future.delayed(Duration(seconds: 2));
  return 12500.50;
}

void main() async {
  var results = await Future.wait([
    fetchOrders(),
    fetchRevenue()
  ]);

  print("Orders: ${results[0]}");
  print("Revenue: ${results[1]}");
}

This pattern reduces overall execution time.


Pattern 3: Fire-and-Forget

Some tasks do not require waiting for completion. They can be triggered and ignored safely.

void logAnalytics() {
  Future(() {
    print("Analytics logged");
  });
}

void main() {
  print("App Started");
  logAnalytics();
  print("Continue App Flow");
}

Use this for logging, metrics, and background notifications.


Pattern 4: Retry Pattern

Network calls often fail. Retry logic improves reliability.

Future fetchData() async {
  await Future.delayed(Duration(seconds: 1));
  throw Exception("Network error");
}

Future retryFetch(int retries) async {
  for (int i = 0; i < retries; i++) {
    try {
      return await fetchData();
    } catch (e) {
      print("Retry attempt ${i + 1}");
    }
  }
  return "Failed after retries";
}

void main() async {
  String result = await retryFetch(3);
  print(result);
}

This pattern is common in API-based systems.


Pattern 5: Timeout Pattern

Timeouts prevent waiting forever on slow operations.

Future slowTask() async {
  await Future.delayed(Duration(seconds: 5));
  return "Completed";
}

void main() async {
  try {
    String result = await slowTask()
        .timeout(Duration(seconds: 2));
    print(result);
  } catch (e) {
    print("Operation timed out");
  }
}

Timeouts improve responsiveness and fault tolerance.


Pattern 6: Stream-Based Pattern

Streams are ideal for handling continuous or real-time data.

Stream numberStream() async* {
  for (int i = 1; i <= 5; i++) {
    await Future.delayed(Duration(seconds: 1));
    yield i;
  }
}

void main() async {
  await for (var value in numberStream()) {
    print(value);
  }
}

Used for sensors, sockets, and live updates.


Pattern 7: Chained Futures

Chaining allows transforming results step by step.

Future getBaseValue() async {
  return 10;
}

void main() {
  getBaseValue()
      .then((value) => value * 2)
      .then((value) => value + 5)
      .then((result) => print(result));
}

Chaining is useful for simple pipelines.


Choosing the Right Pattern

  • Sequential – dependent steps
  • Parallel – independent tasks
  • Fire-and-forget – background work
  • Retry / Timeout – network reliability
  • Streams – continuous data

📝 Practice Exercises

Exercise 1

Create two async functions and execute them in parallel.

Exercise 2

Add timeout handling to a delayed Future.

Exercise 3

Build a stream that emits random numbers every second.


What’s Next?

In the next lesson, you will apply concurrency and async patterns to real-world Dart scenarios, including performance and scalability examples.