Chaining Futures Do Not Execute In Order - dart

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?

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);
}

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));

Related

.Net Maui app completion handlers and iOS HealthKit not working with HttpClient

This works perfectly in Android.
public async Task<double> UploadData()
{
double steps = 0.0;
await _healthData.GetSteps((totalSteps) =>
{
SentrySdk.CaptureMessage("totalSteps = " + totalSteps);
MainThread.BeginInvokeOnMainThread(() =>
{
steps = totalSteps;
//Task.Delay(1000);
});
});
SentrySdk.CaptureMessage("UploadData steps = " + steps);
var fitness = new Fitness();
fitness.Steps = steps;
await _restService.SaveItemAsync(fitness, true);
return steps;
}
In iOS, totalSteps is correct, but steps is still 0 when fitness.Steps = steps runs. Bottom line, I can't get the totalSteps value from inside the _healthData.GetSteps((totalSteps) operation. The Android Google Fit and iOS HealthKit API calls run with completion handlers.
At this stage, I'm just trying to figure out how to upload data (steps, calories, active minutes, distance) to my server.
Does anyone know how to make this work? I can display all the data (steps, calories, active minutes, distance) in a View using an ObservableCollection.
I got this to work by embedding the calls like so.
_healthData.GetHealthPermissionAsync((result) =>
{
if (result)
{
_healthData.FetchSteps((totalSteps) =>
{
_healthData.FetchMetersWalked((metersWalked) =>
{
_healthData.FetchActiveMinutes((activeMinutes) =>
{
_healthData.FetchActiveEnergyBurned((caloriesBurned) =>
{
var fitness = new Fitness();
fitness.Steps = totalSteps;
fitness.Calories = caloriesBurned;
fitness.Distance = metersWalked;
fitness.Minutes = activeMinutes;
_restService.SaveItemAsync(fitness, true);
});
});
});
});
}
});

Neo4j transaction with array of queries with js-neo4j-driver

I want to pass an n-number of cypher-queries to a neo4j-transaction and I am thinking about a good approach.
At the moment I have a working approach that takes the array-item or if it is not available a dummy-query. (Code below)
I believe this is not best-practice. Does anybody know or have an idea how this can be done better?
function Neo4jTransaction(QueryArray) {
const session = driverWrite.session();
const tx = session.beginTransaction();
tx.run(QueryArray[0] || "RETURN 0")
tx.run(QueryArray[1] || "RETURN 0")
tx.run(QueryArray[2] || "RETURN 0")
tx.run(QueryArray[3] || "RETURN 0")
.then(result => {
return tx.commit()
}).then(() => {
session.close()
driverWrite.close()
}).catch(exception => {
console.log(exception)
session.close()
driverWrite.close()
})
}
First, if you have an array, you might want to iterate over it. Second, tx.run() returns a Promise that you need to catch if it fails. In your code, it is called 4 times in a row, but only the last one waits for the result and catches the error. I looks like some lines of the code are missing.
neo4j-driver documentation gives a good example on explicit transactions: https://github.com/neo4j/neo4j-javascript-driver#explicit-transactions
The queries are executed sequentially. If one fails the whole transaction will be rolled back.
async function neo4jTransaction(queryArray) {
const session = driver.session();
const txc = session.beginTransaction();
try {
for (const query of queryArray) {
await txc.run(query || 'RETURN 0');
}
await txc.commit();
} catch (e) {
await txc.rollback();
return Promise.reject(e);
} finally {
await session.close();
}
}

Dart Async Do Something Else Then Wait

I've read several stackoverflow questions, dart documents, and even watched a video on async and await. I haven't found an answer to my question. I would like to call an async method, execute other code, and then wait on the completion of the async task.
Here's an example of what I'm working with. This is my component
Credit credit;
...
Future<Null> getCredit(id) async {
try {
credit = await _creditService.getCredit(id);
}
catch (e) {
errorMessage = e.toString();
}
}
...
void onUpdateCredit(int credit_id) {
getCredit(credit_id);
creditDialogTitle = 'Update Credit';
creditArtistIndex = credit.artist_id;
instrument = credit.instrument;
creditNotes = credit.notes;
creditDialog.open();
}
This code crashes because credit is null when an attempt is made to use it. One way around it is combine the two methods:
Future<Null> onUpdateCredit(id) async {
try {
credit = await _creditService.getCredit(id);
creditDialogTitle = 'Update Credit';
creditArtistIndex = credit.artist_id;
instrument = credit.instrument;
creditNotes = credit.notes;
creditDialog.open();
}
catch (e) {
errorMessage = e.toString();
}
}
Nothing is done is parallel and, if I need the credit some where else in my code, I would have to duplicate the try/catch portion of the method. I could also code it like this:
void onUpdateCredit(int credit_id) {
credit = null;
getCredit(credit_id);
creditDialogTitle = 'Update Credit';
while (credit == null) {//wait a period of time}
creditArtistIndex = credit.artist_id;
instrument = credit.instrument;
creditNotes = credit.notes;
creditDialog.open();
}
In other situations, I do something similar to this in my html with *ngIf="var != null" where var is populated by a future.
Is there a better way than using while (credit == null) ? This example only executes one instruction between the request and the completion so is trivial. I'm sure I'll other situations where I have a lot to do in between. I'm also adding the service method:
Future<Credit> getCredit(int id) async {
try {
String url = "http://catbox.loc/credits/${id.toString()}";
HttpRequest response = await HttpRequest.request(
url, requestHeaders: headers);
Map data = JSON.decode(response.responseText);
final credit = new Credit.fromJson(data);
return credit;
}
catch (e) {
throw _handleError(e);
}
}
Update
Based on #Douglas' answer, this works:
Future<Null> onUpdateCredit(id) async {
Future future = getCredit(id);
creditDialogTitle = 'Update Credit';
await future;
creditArtistIndex = credit.artist_id;
instrument = credit.instrument;
creditNotes = credit.notes;
creditDialog.open();
}
I then eliminated the intervening method.
Future<Null> onUpdateCredit(id) async {
try {
Future<Credit> future = _creditService.getCredit(id);
creditDialogTitle = 'Update Credit';
credit = await future;
creditArtistIndex = credit.artist_id;
instrument = credit.instrument;
creditNotes = credit.notes;
creditDialog.open();
}
catch (e) {
errorMessage = e.toString();
}
}
getCredit(credit_id) does not just kick off an asynchronous call, it also returns a Future object - immediately. Store that object in a local variable, and you can use it later to asynchronously execute additional code when it completes.
There are two ways to use that Future object. The easier and more fluent way requires that you declare onUpdateCredit to be async. Inside an async function, the line await futureObject will cause all code after that line to be executed asynchronously after the Future completes. The complete version of onUpdateCredit using this technique would look like this:
Future<Null> onUpdateCredit(int credit_id) async {
Future future = getCredit(credit_id);
creditDialogTitle = 'Update Credit';
await future;
creditArtistIndex = credit.artist_id;
instrument = credit.instrument;
creditNotes = credit.notes;
creditDialog.open();
}
The other way is to explicitly register the rest of your code as a callback using .then(). That would look like this:
void onUpdateCredit(int credit_id) {
Future future = getCredit(credit_id);
creditDialogTitle = 'Update Credit';
future.then((_) => {
creditArtistIndex = credit.artist_id;
instrument = credit.instrument;
creditNotes = credit.notes;
creditDialog.open();
});
}
Note that in either case, if the exception path occurs in getCredit(id) you will get errors for credit not being set. If you truly want the exception to be swallowed silently, you should have its handler fill in a default value for credit so that code that assumes it completed normally will still work.
Also note that your while loop version would fail - Dart, like JavaScript, is not truly multithreaded, and busy waiting like that will block the event loop forever, preventing the code that would set credit from ever running.
A short summary of how async and await work in general:
Future someFunc(args) async {
...
return value;
}
is equivalent to:
Future someFunc(args) {
return new Future(() => {
...
return value;
}
}
The code inside gets executed on a later iteration of the event loop, and the returned future completes either successfully with value or exceptionally with anything thrown in that code.
Meanwhile this:
try {
value = await someFutureObject;
...more code here...
} catch (e) {
...exception handling here...
}
is equivalent to:
someFutureObject.then((value) => {
...more code here...
}).catchError((e) => {
...exception handling here...
});
The most common use case is someVar = await someAsyncCall();, but you can save the Future itself by omitting await, or you can await on an arbitrary Future object no matter where it comes from.
However - and this is what makes await and async so incredibly convenient - you can have 5 different exit points from the async function interspersed with three await calls (in the same async function) in assorted places inside 13 nested loops, switches, try/catch, and if blocks, and Dart will automatically figure out the necessary tree of callbacks to make it all follow the same code path as if all the calls were synchronous.

Async Futures running in sequence to completion

I encountered the following example (Example 1 below) of Futures which caused me to wonder if I could alter the way that I was handling Futures and remove all of the nested function calls that preserve order of processing, which however result in indentation which I find a bit messy.
The altered version of my program did not work however. It did not preserve the order of processing and did not “wait” for function to complete. For example, before returning from the first call (fGetUserInput), another subsequent function was called.
Why is it that in Example 1, all of the “1st level” “new Future”s processed sequentially, however in Example 2, my altered code, the order of processing is not preserved. While the call to fGetUserInput is being processed, one of the Futures that follows it is processed?
Is it perhaps that “Example 1” only “works” because all of the statements are synchronous?
I came across a reference to “runAsync”. Can that be used to achieve what I want? (process in sequence without all of the indentation).
// Example 1. Code that I encountered for Futures //
import 'dart:async';
main() {
new Future(() => print('1'))
.then((_) => print('a'))
.then((_) => print('b'));
new Future(() => print('2'))
.then((_) => print('c'))
.then((_) => print('d'));
new Future(() => print('3'))
.then((_) =>
new Future(() => print('e'))
.then((_) => print('f'))
);
new Future(() => print('4'))
.then((_) =>
new Future(() => print('g'))
.then((_) => print('d'))
);
}
The above results in the following console output order :-
1 a b 2 c d 3 4 e f g d
Which I thought made sense.
Therefore, I modified my code to test it as follows :-
// Example 2. Altered version of my code which //
// does not preserve the order of processing, //
// which is necessary for program to function. //
new async.Future(() => fGetUserInput())
.then((lInput) {
iMaxIters = int.parse(lInput[4]);
tClearTable = (lInput[5] == "y");
iDivisor = fInitialize(iMaxIters);
tgPrint = false; // printing off
sUri =
"postgres://${lInput[1]}:${lInput[2]}#localhost:5432/${lInput[3]}";
sStartTime = lInput[7];
})
.catchError((oError) => fFatal("Get User Input", oError));
new async.Future(() => fConnectToDb(sUri, sStartTime))
.then((bool tConnected) {
if (ogDb == null)
fFatal("Unable to connect to database", "");
print ("Processing database ......");
})
.catchError((oError) => fFatal("Connect to Db", oError));
new async.Future(() => fClearTable(tClearTable))
.then((sResult) => print (sResult+"\n"))
.catchError((oError) => fFatal("Clear Table", oError));
new async.Future(() => fProcessInserts(iMaxIters, iDivisor))
.then((sResult) => print (""))
.catchError((oError) => fFatal("Process Inserts", oError));
new async.Future(() => fSetupRandKeys())
.then((sResult) => print (""))
.catchError((oError) => fFatal("Setup Random Keys", oError));
new async.Future(() => fProcessUpdates(iMaxIters, iDivisor))
.then((sResult) {
String sTotValue = fFormatAmount(igGrandTotAmt, true, 2);
fPrint ("Grand Total added to database = \$${sTotValue}");
ogDb.close();
exit(0);
})
.catchError((oError) => fFatal("Process Updates", oError));
}
void fFatal (String sMessage, Error oError) {
print("\n\nFatal Error. $sMessage\n${oError}");
exit(1);
}
async.Future<String> fProcessInserts(int iMaxIters, int iDiv) {
async.Completer oCompleter = new async.Completer<String>();
int iTot = 0;
Function fLoop;
print ("\nProcessing Inserts ......");
fResetAndStartWatch();
The following is my code prior to the above changes, and the following Example 3 appears to work OK. I don't like the extent of indentation, and in situations with more function calls, that would increase the extent of indentation. I was hoping for a more elegant way to do it.
// Example 3: The original version of my code //
// which does preserve the order of processing //
void main() {
print("");
String sCheckPoint = "Get User Input";
fGetUserInput()
.then((lInput) {
int iMaxIters = int.parse(lInput[4]);
bool tClearTable = (lInput[5] == "y");
int iDiv = fInitialize(iMaxIters);
tgPrint = false; // printing off
String sUri =
"postgres://${lInput[1]}:${lInput[2]}#localhost:5432/${lInput[3]}";
sCheckPoint = "Connect to Database";
fConnectToDb(sUri, lInput[7]).then((bool tConnected) {
if (ogDb == null)
fFatal(sCheckPoint, "Unable to conenct to Db");
print ("Processing database ......");
sCheckPoint = "Clear Table";
fClearTable(tClearTable).then((sResult) {
print (sResult+"\n");
sCheckPoint = "Process Inserts";
fProcessInserts(iMaxIters, iDiv).then((sResult) {
print;
sCheckPoint = "Set-up Random Keys";
fSetupRandKeys().then((sResult) {
print;
sCheckPoint = "Process Updates";
fProcessUpdates(iMaxIters, iDiv).then((sResult) {
String sTotValue = fFormatAmount(igGrandTotAmt, true, 2);
fPrint ("Grand Total added to database = \$${sTotValue}");
ogDb.close();
exit(0);
});
});
});
});
});
})
.catchError((oError) => fFatal(sCheckPoint, oError));
}
void fFatal (String sMessage, Error oError) {
print("\n\nFatal Error. $sMessage\n${oError}");
exit(1);
}
async.Future<String> fProcessInserts(int iMaxIters, int iDiv) {
async.Completer oCompleter = new async.Completer<String>();
int iTot = 0;
Function fLoop;
print ("Processing Inserts ......");
fResetAndStartWatch();
Remember that you can chain futures, which will reduce your indentation by quite a bit.
The downside is that you don't get nested scopes, which can be useful if you have more than one value to propagate between async blocks, but that can be worked around in a few ways.
Here's you example 3 with chaining:
// Example 3 with chaining
void main() {
String checkPoint = "Get User Input";
getUserInput().then((input) {
int maxIters = int.parse(input[4]);
bool clearTable = (input[5] == "y");
int div = initialize(maxIters);
shouldPrint = false; // printing off
String uri =
"postgres://${input[1]}:${input[2]}#localhost:5432/${input[3]}";
checkPoint = "Connect to Database";
return connectToDb(uri, input[7]).then((bool connected) {
if (db == null)
fatal(checkPoint, "Unable to conenct to Db");
print ("Processing database ......");
checkPoint = "Clear Table";
return clearTable(shouldClearTable);
}).then((result) {
print (result+"\n");
checkPoint = "Process Inserts";
return processInserts(maxIters, div);
}).then((result) {
print('');
checkPoint = "Set-up Random Keys";
return setupRandKeys();
}).then((result) {
print('');
checkPoint = "Process Updates";
return processUpdates(maxIters, div);
}).then((result) {
String totValue = formatAmount(grandTotAmt, true, 2);
print("Grand Total added to database = \$${totValue}");
return db.close();
// exit(0); pretty much never call exit()
});
}).catchError((error) => fatal(checkPoint, error));
}
Edit: Oops, looking more closely I got bit by the scoping problem... I added a level of nesting just to capture the needed vars in a scope accessible by the following blocks. I'm also removing the hungarian-ish notation, because... don't do that in Dart :)

Future Completer.future

Could someone please explain to me what is wrong with the following code.
The line .then((result) {window.alert("Record = ${oDbRec}");}) displays the following :
Record = Instance of '_ThenFuture#0x2900bd4a'
What I want is the result from the getObject(). The code:
fHtmDisplayOneClient(String sKey) {
var oDbRec;
oDbRec = fDbSelectOneClient(sKey)
.then((result) {window.alert("Record = ${oDbRec}");})
..catchError((oError) {window.alert("Error from fDbSelectOneClient. ${oError}");
});
}
Future fDbSelectOneClient(String sKey) {
var completer = new Completer();
idb.Transaction oDbTxn = ogDb1.transaction(sgTblClient, 'readwrite');
idb.ObjectStore oDbTable = oDbTxn.objectStore(sgTblClient);
idb.Request oDbReqGet = oDbTable.getObject(sKey);
oDbReqGet.onSuccess.first.then((val) => completer.complete(oDbReqGet.result));
oDbReqGet.onError.first.then((err) => completer.completeError(err));
return completer.future; // return the future
}
I think you want what the future returns, which is the 'result' object passed to .then()
.then((result) => print('$result'));

Resources