Dart Lesson 43 – Dart Concurrency | Dataplexa

Dart Concurrency

In this lesson, you will learn how Dart handles concurrency and how applications can perform multiple tasks efficiently without blocking execution.

Concurrency is essential for building responsive applications, especially when dealing with file operations, APIs, background tasks, and heavy computations.


What Is Concurrency?

Concurrency means handling multiple tasks at the same time. In Dart, this does not mean running everything in parallel by default.

Instead, Dart uses an event-driven, non-blocking model that keeps applications fast and responsive.


Why Concurrency Is Important

  • Prevents application freezing
  • Improves performance
  • Handles network requests efficiently
  • Processes background tasks safely

Without concurrency, a single slow operation could block your entire program.


Dart Concurrency Model

Dart uses a single-threaded event loop model with:

  • Event Queue
  • Microtask Queue
  • Isolates (for true parallelism)

This design avoids common threading issues like race conditions.


Event Loop in Dart

The event loop processes tasks in this order:

  1. Microtasks
  2. Event queue tasks

Understanding this order helps predict execution behavior.


Simple Concurrency Example

This example shows non-blocking execution using Future.

void main() {
  print("Start");

  Future.delayed(Duration(seconds: 2), () {
    print("Async Task Completed");
  });

  print("End");
}

Output order:

  • Start
  • End
  • Async Task Completed

The program does not wait for the delayed task.


Using Futures for Concurrency

Futures represent values that will be available later. They allow Dart to run tasks asynchronously.

Future fetchOrderTotal() async {
  await Future.delayed(Duration(seconds: 1));
  return 250;
}

void main() async {
  int total = await fetchOrderTotal();
  print("Order total: $total");
}

This pattern is widely used in real applications.


Concurrency with Multiple Futures

You can execute multiple async tasks concurrently using Future.wait.

Future fetchUser() async {
  await Future.delayed(Duration(seconds: 1));
  return "Alice";
}

Future fetchBalance() async {
  await Future.delayed(Duration(seconds: 2));
  return 5000;
}

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

  print("User: ${results[0]}");
  print("Balance: ${results[1]}");
}

Both tasks run concurrently instead of sequentially.


Microtasks in Dart

Microtasks have higher priority than event tasks. They are executed before the event queue.

void main() {
  scheduleMicrotask(() {
    print("Microtask executed");
  });

  Future(() {
    print("Event task executed");
  });
}

Microtasks are useful for small, urgent operations.


Using Isolates for True Parallelism

Dart uses Isolates to run code in parallel. Each isolate has its own memory and event loop.

This is useful for CPU-intensive tasks.

import 'dart:isolate';

void heavyTask(SendPort sendPort) {
  int sum = 0;
  for (int i = 0; i < 100000000; i++) {
    sum += i;
  }
  sendPort.send(sum);
}

void main() async {
  ReceivePort receivePort = ReceivePort();
  await Isolate.spawn(heavyTask, receivePort.sendPort);

  receivePort.listen((message) {
    print("Result from isolate: $message");
  });
}

This prevents blocking the main thread.


Real-World Use Cases

  • Fetching data from multiple APIs
  • Processing files in background
  • Handling user interactions smoothly
  • Running heavy calculations

Common Mistakes

  • Blocking code in async functions
  • Overusing isolates unnecessarily
  • Ignoring error handling in Futures

📝 Practice Exercises

Exercise 1

Create two Futures that complete after different delays and print their results.

Exercise 2

Use Future.wait to fetch multiple values concurrently.

Exercise 3

Run a CPU-intensive loop using an isolate.


What’s Next?

In the next lesson, you will learn about Dart Asynchronous Patterns and how to design scalable async workflows.