Flutter: Prevent deadlocks in a task queue when tasks enqueue other tasks
07:25 04 Jun 2025

I'm trying to write a task queue class in flutter, but I need a way of detecting if a task is already running to prevent deadlocks.

import 'dart:async';

Future main() async {
  final a = doSomething(1);
  final b = doSomethingElse(2);
  final c = doSomething(3);
  
  await Future.wait([a, b, c]);

  print('done');
}

final queue = TaskQueue();

Future doSomething(int count) async {
  return queue.enqueue(() async {
    await Future.delayed(const Duration(milliseconds: 500));
    print('something #$count');
    await doSomethingElse(count);
  });
}

Future doSomethingElse(int count) {
  return queue.enqueue(() => print('something else #$count'));
}

typedef Task = FutureOr Function();

class TaskQueue {
  final _queue = <_TaskEntry>[];
  var _running = false;

  bool get isRunning => _running;

  Future _begin(Future first) async {
    _running = true;
    await first;
    while (true) {
      final task = _queue.firstOrNull;
      if (task == null) {
        break;
      }

      _queue.removeAt(0);
      await task.run();
    }

    _running = false;
  }

  Future enqueue(Task task) {
    if (_running) {
      final completer = Completer();
      _queue.add(_TaskEntry(task, completer));
      return completer.future;
    }

    final result = task();
    if (result is! Future) {
      return Future.value(result);
    }

    _begin(result);
    return result;
  }
}

class _TaskEntry {
  final Task task;
  final Completer completer;

  _TaskEntry(this.task, this.completer);

  Future run() async {
    try {
      final result = await task();
      completer.complete(result);
    } catch (error, stack) {
      completer.completeError(error, stack);
    }
  }
}

In this example, the program freezes because the doSomething method awaits doSomethingElse, which queues another task, but the queue never starts that task as it is still waiting for doSomething to complete. Is there a way of detecting whether the current TaskQueue is already running a task inside the enqueue method, and running it immediately?

The expected output is

something #1
something else #1
something else #2
something #3
something else #3
done
flutter dart asynchronous