How to do several asynchronous I/O actions in Dart cleanly (Isolates)? - dart

In Dart, there is a concept of Isolates. I have an application (that I'm experimenting in Dart) that has lots of asynchronous IO where each call (they are database calls) are dependent on the previous one. So I have ended up in a nested callback hell.
I was wondering if Isolates could solve that nested callback soup, but it looks a bit verbose and I'm not sure if it fits it well.
There are also Generators proposed in the next ECMAScript Harmony which could solve these things, but how would you currently do lots of asynchronous IO in Dart in a clean way?

You can use Future's and Completers to chain work together. The following future returns the result of a 'ls' command from a process:
Future<String> fetch(String dir) {
final completer = new Completer();
Process process = new Process.start('ls', [dir]);
process.exitHandler = (int exitCode) {
StringInputStream stringStream = new StringInputStream(process.stdout);
stringStream.dataHandler = () {
String content = stringStream.read();
completer.complete(content);
process.close();
};
};
process.errorHandler = (var error) {
completer.completeException(error);
return true;
};
return completer.future;
};
which you can then chain together like this:
fetch('/').then((val) => fetch("/usr").then((val) => fetch("/tmp")));
Not the most pretty solution but this is what I get by with now.

Related

Async-await in dart runs synchronously?

I am trying out the Future with async await for asynchronous programming.
The code i tested is a simple Future.
//order a new coffee perhaps!
Future<String> order(String newOrder){
final String Function() func = ()=> "${newOrder} is requested!";
final _order = Future.delayed(Duration(seconds: 5),func);
return _order;
}
When i run this code like a promise.
order("promised order")
.then((result) => print(result))
.catchError((err){
print(err.error);
});
This behaves like a asynchronous non-blocking code
However the same code when i run it with async/await behaves like a synchronous code and blocks all the other code, it waits 5 secs to run the next line.
void main(List<String> arguments) async {
final _myOrder = await order('Lattee mocha');
print(_myOrder);
//...all the other code waits for
}
So i thought async/await is same as futures, where non-blocking..
How come it blocks the other code execution ?
The whole point of the async/await pattern is to wait (hence await) until the Future completes and only then continue with this code execution.
Think of it as a way to not have endless .then() chains and much simplified error handling.
If you want to not wait, then you just leave out the await keyword. Obviously, if you don't (a)wait, you don't get the result.
You can still use classic .then() chains when it suits your purpose better in your program.
What people talk about when they say "non blocking" they mean that when the compiler sees the await keyword, it knows to keep the rest of the program, the event loops, animations etc running and not block the complete program.

How does Dart run asynchronous code without blocking?

How does Dart run asynchronous code without blocking?
For example:
void test(User user) async {
print('test');
String fullName = user.firstName + ' ' + user.lastName;
final result = await sendRequest(fullName);
print('${result}');
}
I understand that when Dart runs async function, it executes code before await and then wraps the remaining code into Future and puts it into Event Loop. But, how does that Future what we're awaiting for (await sendRequest(fullName)) not blocking running the other, synchronous code? We should wait for request for complete but it also requires some code to check that we've received or not received response. How does it not block other operations, like button clicks?
The function returns at the first await. It returns a future which will later be completed (which is then ignored because of the void return type). Everything else in the function happens in reaction to callbacks from other futures (or streams, if you use await for).
The async function here is basically rewritten into something like:
void test(User user) {
print('test');
String fullName = user.firstName + ' ' + user.lastName;
return sendRequest(fullName).then((final result) { // <-- Returns here, synchronously.
print('${result}'); // <-- Gets called by the future of `sendRequst(fullName)`.
});
}
This transformation is similar to a continuation-passing style transformation in the it takes the continuation (the control flow in the body of the function after the await) and makes it into a function.
(In reality, the transformation is more complicated because it needs to account for loops, try/catch/finally, and even break statements. Because of that, even a simple example like this one will likely be converted into something more complicated than absolutely necessary).

What is the purpose of `FutureOr`?

Dart offers a FutureOr class, that allows writing:
FutureOr<int> future;
future = 42; // valid
future = Future.value(42); // also valid
I would assume that FutureOr would be useful to remove the unnecessary delay caused by the event loop if the value can be read synchronously.
But that doesn't seem to be the case, as showcased by:
import 'dart:async';
void main() async {
print('START');
futureOrExample();
print('END');
}
void futureOrExample() async {
FutureOr<int> futureOr = 42;
print('before await');
await futureOr;
print('end await');
}
which prints:
START
before await
END
end await
when I would expect:
START
before await
end await
END
In that case, why does FutureOr (or more generally await 42) work this way?
Similarly, what's the purpose of FutureOr in that situation since it produces the same result as Future?
I know that I could use SynchronousFuture to achieve the desired result, but I'm just trying to understand what's the use of FutureOr.
The use of FutureOr, as introduced with Dart 2, is to allow you to provide either a value or a future at a point where the existing Dart 1 API allowed the same thing for convenience, only in a way that can be statically typed.
The canonical example is Future.then. The signature on Future<T> is Future<R> then<R>(FutureOr<R> action(T value), {Function onError}).
The idea is that you can have an action on the future's value which is either synchronous or asynchronous. Originally there was a then function which took a synchronous callback and a chain function which took an asynchronous callback, but that was highly annoying to work with, and in good Dart 1 style, the API was reduced to one then method which took a function returning dynamic, and then it checked whether it was a future or not.
In Dart 1 it was easy to allow you to return either a value or a future. Dart 2 was not as lenient, so the FutureOr type was introduced to allow the existing API to keep working. If we had written the API from scratch, we'd probably have done something else, but migrating the existing asynchronous code base to something completely different was not an option, so the FutureOr type was introduced as a type-level hack.
The await operation was also originally defined to work on any object, long before FutureOr existed. For consistency and smaller code, an await e where e evaluated to a non-future would wrap that value in a future and await that. It means that there is only one quick and reusable check on a value (is it a future, if not wrap it), and then the remaining code is the same. There is only one code-path.
If the await worked synchronously on non-Future values, there would have to be a synchronous code path running through the await, as well as an asynchronous path waiting for a future. That would potentially double the code size, for example when compiling to JavaScript (or worse, if there were more awaits in the same control flow, you could get exponential blow-up for a naive implementation). Even if you avoided that by just calling the continuation function synchronously, it would likely be confusing to some readers that an await would not introduce an asynchronous gap. A mistake around that can cause race conditions or things happening in the wrong order.
So, the original design, predating FutureOr, was to make all await operations actually wait.
The introduction of FutureOr did not change this reasoning, and even if it did, it would now be a breaking change to not wait in places where people expect their code to actually give time for other microtasks to run.
The await keyword always lock the function execution.
Writing:
await 42
Is equivalent to:
await Future.value(42)
The reason being:
This is how await works in Javascript
it makes the behavior of await consistent.
Now, what's the purpose of FutureOr then?
FutureOr was never intended as a way to potentially make await synchronous.
Instead, it is an implementation detail of Future.
Without FutureOr, writing the following would not compile:
Future(() {
return 42; // compile error, not a Future
});
Future<int> future;
future.then((value) {
return value * 2; // compile error, not a Future
});
Instead, we would have to wrap all values in a Future.value like so:
Future<int> future;
future.then((value) {
return Future.value(value * 2);
});
for those who are still confused
I found good explanation https://itnext.io/what-is-futureor-in-dart-flutter-681091162c57 without diving into details
This piece of code could explain the target and real use cases of FutureOr
abstract class IDBService {
FutureOr<String> fetch();
}
class FirebaseRemoteService extends IDBService {
#override
Future<String> fetch() async => await 'data';
}
class LocalHiveDbService extends IDBService {
#override
String fetch() => 'data';
}
so in implementations of IDBService
the return type can be Future or String at the same time now!
Coming late to the discussion.
Updating my Dart comprehension - pardon my C++/JS -ish approach.
Seems like this would be useful for singleton initiation. Consider following:
import 'dart:async';
class AClass {
static String _info = '';
static FutureOr<String> get info async {
if (_info.isEmpty) {
print('--> is empty...');
_info = await Future.delayed(Duration(seconds:2),
() => "I'm alive!!");
}
else {
print('--> not empty');
}
return _info;
}
}
Future<void> main() async {
String info = await AClass.info;
print('Fist call: ' + info);
info = await AClass.info;
print('Second call: ' + info);
}
It works as expected - in either case, whether the _info member has been instantiated or not, the getter returns a valid string.
It works fine if I just use a Future<String> specifier in the getter, too. The current implementation makes FutureOr seem mostly like a self-documentation exercise (can return a Future<String> or a String...)
But, even if await currently always locks the execution, a future update may allow it to work as expected, in which case using the FutureOr construct would anticipate updates.
(Aside: I imagine this example could be condensed using an Optional wrapping the _info member, but that's a different exercise...)
I needed to use FutureOr today. I wanted to call a function that might be asynchronously (not always).
String callbackOne() => "hello";
Future<String> callbackTwo() async => (await Future.delayed(Duration(seconds: 1),() => "This is a sentence"));
Problem
I can do getLengthOfResult(callbackOne), but not getLengthOfResult(callbackTwo). Conversely, if accept an async callback, I can't use the sync callback.
DartPad
Future<int> getLengthOfResult(String Function() callback) async {
return callback().length;
}
Solution
DartPad
Future<int> getLengthOfResult(FutureOr<String> Function() callback) async {
// I can await on callbackOne, even though it returns a String.
final result = await callback();
return result.length;
}
main() {
getLengthOfResult(callbackOne);
getLengthOfResult(callbackTwo);
}

How to wait for openRead function to complete on one file before moving on to the next?

I'm trying to load multiple files, in a certain order, inside of Dart. The files are of a modified GTFS (General Transit Feed Specification) type and are such interconnected through 'ids' between the files. Because of this I'm trying to load each file one by one but due to their large size I am using the openRead method of the File class to stream them in line-by-line using the LineSplitter transformer.
How they are being loaded in (start is the byte offset of the openRead method)
Stream<List<int>> getFileStream(String fileName, {int start}) => File(fileName).openRead(start);
// Example Stream
Stream<List<int>> stops = getFileStream("stops.txt", start: 117);
Stream<List<int>> routes = getFileStream("routes.txt", start: 131);
// How the stream data is being read
stops
.transform(utf8.decoder)
.transform(LineSplitter())
.listen(
(String line) {
List<String> values = line.split(',');
// Do stuff..
},
onDone: () { },
onError: (e) { print(e.toString()); }
);
routes
.transform(utf8.decoder)
.transform(LineSplitter())
.listen(
(String line) {
List<String> values = line.split(',');
// Do stuff..
// Error occurs here as well since the processing taking place here depends on the processing which takes place when the stops file is read
},
onDone: () { },
onError: (e) { print(e.toString()); }
);
However since I'm loading in multiple files within the same function and they are all streams with a listen callback set and they depend on the processing of the files that came before them to be COMPLETELY finished the program is producing errors since all the files are being read at once line by line and the processing of the other files has not finished.
Ideally, I would like to use an await for (String line in stops) line or something similar, however that produces the following error which I do not know how to solve:
The type 'Stream<List<int>>' used in the 'for' loop must implement Stream with a type argument that can be assigned to 'String'.
This error still shows up even if I do the .transform calls on the stream before the await for line.
I've also tried chaining together the onDone methods of the streams which produced an abomination of code which still didn't work (Lists that were created and added to within the function were empty upon returning???)
It would be nice to use the await for syntax as that produces cleaner code, however I do not want to pollute the whole function tree with async, await functions especially the main() function.
The only thing that worked was using the Completer class from dart:async however the onDone methods needed to be chained for this and the code was barely readable.
Any help as well as some guidance on Futures and async/await would be appreciated.
Have you tried something like this?
await for (var line in stops
.transform(utf8.decoder)
.transform(LineSplitter())) {
List<String> values = line.split(',');
// Do stuff..
// Error occurs here as well since the processing taking place here depends on the processing which takes place when the stops file is read
}

Is there a more F# idiomatic way for code using TPL?

I'm converting the following C# code to F#. (https://github.com/confluentinc/confluent-kafka-dotnet/blob/master/examples/AvroGeneric/Program.cs)
var consumeTask = Task.Factory.StartNew(() =>
{
while (true)
{
consumer.Poll(100);
}
});
consumeTask.Wait();
Should it be replaced by Async workflow? BTW, is it a way not to use Poll?
For a more F#-idiomatic way of using Kafka in general, take a look at the Kafunk library from Jet.com. It has a nice F# API wrapping the Confluent .NET Kafka library, and in my experience it's also quite performant.
For TPL Tasks, you can use F# Async Workflows in much the same way:
let poller =
async {
while true do
consumer.Poll 100
}
poller |> Async.Start
If you want to use a Task inside your Async Workflow, you can use Async.AwaitTak, or you can blend them together using a custom workflow, like this one.

Resources