Waitgroups in Dart

Our example demonstrates how to use asynchronous programming in Dart to wait for multiple tasks to complete. We’ll use Future and Completer to achieve similar functionality to Go’s WaitGroups.

import 'dart:async';

// This is the function we'll run in every asynchronous task.
Future<void> worker(int id) async {
  print('Worker $id starting');
  
  // Sleep to simulate an expensive task.
  await Future.delayed(Duration(seconds: 1));
  print('Worker $id done');
}

void main() async {
  // This Completer is used to wait for all the
  // asynchronous tasks launched here to finish.
  final completer = Completer<void>();
  var count = 0;

  // Launch several asynchronous tasks and increment the counter for each.
  for (var i = 1; i <= 5; i++) {
    count++;
    
    // Wrap the worker call in a Future that makes sure to decrement
    // the counter when this worker is done. This way the worker
    // itself does not have to be aware of the concurrency primitives
    // involved in its execution.
    worker(i).then((_) {
      count--;
      if (count == 0) {
        completer.complete();
      }
    });
  }

  // Block until all tasks are complete.
  await completer.future;

  // Note that this approach has no straightforward way
  // to propagate errors from workers. For more
  // advanced use cases, consider using packages like
  // 'package:async/async.dart' which provides more sophisticated
  // asynchronous primitives.
}

To run the program:

$ dart run waitgroups.dart
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 1 done
Worker 2 done
Worker 3 done
Worker 4 done
Worker 5 done

The order of workers starting up and finishing is likely to be different for each invocation.

In this Dart version:

  1. We use Future<void> to represent asynchronous tasks, similar to goroutines in Go.
  2. Instead of a WaitGroup, we use a Completer and a counter to keep track of running tasks.
  3. The worker function is marked as async and uses await for the delay.
  4. In the main function, we launch tasks using a loop, incrementing the counter for each.
  5. When each task completes, we decrement the counter and check if all tasks are done.
  6. We use await completer.future to block until all tasks are complete, similar to wg.Wait() in Go.

This approach provides similar functionality to Go’s WaitGroups, allowing us to wait for multiple asynchronous tasks to complete before proceeding.