Ryan Moeller
Ryan Moeller

Reputation: 151

Chaining Futures Do Not Execute In Order

I'm currently reading variables from a Bluetooth device. This obviously takes an undetermined amount of time, so I am using futures (This method is readCharacteristic in my code down below).

More than one read operation cannot take place at a time - if a second read operation is started while a first operation is still in progress, Flutter will throw an error.

My understanding was that chaining futures together using .then() would only allow the next statement to execute when the previous call had finished. This idea seems to be true until I try to read a third value - that is when the error is thrown, because of the overlapping read events.

Here is my code:

readCharacteristic(scanDurationCharacteristic)
    .then((list) => sensorScanDuration = list[0].toDouble())
    .then((_) {
  readCharacteristic(scanPeriodCharacteristic)
      .then((list) => sensorScanPeriod = list[0].toDouble());
}).then((_) {
  readCharacteristic(aggregateCharacteristic)
      .then((list) => sensorAggregateCount = list[0].toDouble());
}).then((_) {
  readCharacteristic(appEUICharacteristic)
      .then((list) => appEUI = decimalToHexString(list));
}).then((_) {
  readCharacteristic(devEUICharacteristic)
      .then((list) => devEUI = decimalToHexString(list));
}).then((_) {
  readCharacteristic(appKeyCharacteristic)
      .then((list) => appKey = decimalToHexString(list));
});

What is a better way to ensure that these read events will not overlap?

Upvotes: 3

Views: 3798

Answers (2)

Rémi Rousselet
Rémi Rousselet

Reputation: 277707

Although R.C Howell answer is correct, prefer using async/await keywords instead. This is much more readable and you're less likely to make an error

Future<void> scanBluetooth() async {
  sensorScanDuration = (await readCharacteristic(scanDurationCharacteristic))[0].toDouble();
  sensorScanPeriod = (await readCharacteristic(scanPeriodCharacteristic))[0].toDouble();
  sensorAggregateCount = (await readCharacteristic(aggregateCharacteristic))[0].toDouble();
  appEUI = await readCharacteristic(appEUICharacteristic).then(decimalToHexString);
  devEUI = await readCharacteristic(devEUICharacteristic).then(decimalToHexString);
  appKey = await readCharacteristic(appKeyCharacteristic).then(decimalToHexString);
}

Upvotes: 11

R. C. Howell
R. C. Howell

Reputation: 1081

If you would like to chain Futures, you must return the previous Future from within the then method of the previous Future.

The documentation says to chain like so,

expensiveA()
    .then((aValue) => expensiveB())
    .then((bValue) => expensiveC())
    .then((cValue) => doSomethingWith(cValue));

Which is the same as,

expensiveA()
    .then((aValue) {
        return expensiveB();
    }).then((bValue) {
        return expensiveC();
    }).then((cValue) => doSomethingWith(cValue));

As this applies to your case,

readCharacteristic(scanDurationCharacteristic)
    .then((list) {
        sensorScanDuration = list[0].toDouble();
        return readCharacteristic(scanPeriodCharacteristic);
    }).then((list) {
        sensorScanPeriod = list[0].toDouble());
        return readCharacteristic(aggregateCharacteristic);
    }).then((list) {
        sensorAggregateCount = list[0].toDouble());
        return readCharacteristic(appEUICharacteristic);
    }).then((list) {
        appEUI = decimalToHexString(list));
        return readCharacteristic(devEUICharacteristic);
    }).then((list) {
        devEUI = decimalToHexString(list));
        return readCharacteristic(appKeyCharacteristic);
    }).then((list) => appKey = decimalToHexString(list));

Upvotes: 7

Related Questions