Home > other >  Return synchronously called Futures asynchronously in order of execution in queue in Dart
Return synchronously called Futures asynchronously in order of execution in queue in Dart

Time:10-10

void main() async {
  Future<void> asyncFunction(time, value) async {
    return await Future.delayed(Duration(seconds: time), () => print(value));
  }

  print('1');
  asyncFunction(2, "A");
  asyncFunction(1, "B");
  asyncFunction(0, "C");
  print('2');
}

As per the code above, I'm trying to get the code to print as:

1
2
A
B
C

I want to execute all the functions synchronously and send the asyncFunctions (Futures) off to a sort of queue that then executes those in the order they are recieved. Each function must wait for the previous function to complete it's Future before it then itself executes.

I've been trying around with streams but not getting it right.

CodePudding user response:

Does this work?

void main() async {
  Future<void> asyncFunction(time, value) async {
    return await Future.delayed(Duration(seconds: time), () => print(value));
  }

  print('1');
  print('2');
  await asyncFunction(2, "A");
  await asyncFunction(1, "B");
  await asyncFunction(0, "C");
}

CodePudding user response:

OK I managed to figure something redimentary out:

import 'dart:async';
import 'dart:collection';
import 'dart:math';

var streamController = StreamController<String>();
Queue queue = Queue();
bool busy = false;

Future<void> asyncFunction(value) async {
  await Future.delayed(Duration(seconds: Random().nextInt(4)));
  return print(value);
}

void main() async {
  streamController.stream.listen((letter) async {
    queue.add(letter);
    if (!busy) {
      busy = true;
      while (busy) {
        await asyncFunction(queue.first);
        queue.removeFirst();
        if (queue.isEmpty) {
          busy = false;
        }
      }
    }
  });

  print("1");
  streamController.add("A");
  streamController.add("B");
  streamController.add("C");
  streamController.add("D");
  print("2");
  streamController.add("E");
  streamController.add("F");
  streamController.add("G");
  streamController.add("H");
  print("3");
  print("-");
}

Gives the desired result of:

1
2
3
-
A
B
C
D
E
F
G
H

CodePudding user response:

There is no way to make Future.delayed(2, ...) complete before Future.delayed(1, ...). That just isn't how time works. What you instead could do is to rely on return values instead of side-effects, and then you could build a List of Futures and use Future.wait to collect the results and to print them in the original order:

void main() async {
  Future<dynamic> asyncFunction(time, value) async {
    await Future.delayed(Duration(seconds: time));
    return value;
  }

  var futures = <Future<dynamic>>[];
  
  print('1');
  futures.add(asyncFunction(2, "A"));
  futures.add(asyncFunction(1, "B"));
  futures.add(asyncFunction(0, "C"));
  print('2');
  
  var results = await Future.wait(futures);
  for (var result in results) {
    print(result);
  }
}

The above approach has the benefit of allowing the multiple asynchronous operations from potentially running in parallel.

Alternatively, you could delay constructing the Future.delayed(...) objects so that each one isn't created until the previous one completes:

void main() async {
  Future<void> asyncFunction(time, value) async {
    await Future.delayed(Duration(seconds: time), () => print(value));
  }

  var queue = <Future<void> Function()>[];
  
  print('1');
  queue.add(() => asyncFunction(2, "A"));
  queue.add(() => asyncFunction(1, "B"));
  queue.add(() => asyncFunction(0, "C"));
  print('2');
  
  for (var thunk in queue) {
    await thunk();
  }
}
  • Related