Dart - make long running synchronous function asynchronous - dart

I have a function that might take a few seconds to execute, and it is synchronous. Does:
String slowFunction() { ... }
...
Future<String>(() => slowFunction());
change it to asynchronous?
If I need its result in the next step, does this code make sense?
Future<void> anotherFunction() async {
// other async calls with Futures and await
...
final result = await Future<String>(() => slowFunction());
print(result);
...
// do something else with result
}
Seems kind of strange to create a Future only to immediately await on it. Should I just call the function? I guess it kind of 'yields' and allows other code to be executed before, but does such code have any use?

There is no point in making a process that is inherently synchronous and dressing it up as an asynchronous one. This is due to how asynchronicity (more generally referred to as "concurrency") works, not just in Dart but in general. Concurrency is just a programming trick to make multiple operations run interleaved with each other in the same thread, giving the illusion of true parallelism (which is where different threads or processes run simultaneously). This allows processes that would normally block while waiting for a resource to be put off until later as the program carries on with other things.
If you were to take a synchronous process that blocks due to work being actively done, either the program will block anyway as the "async" code executes just as it would otherwise do or the program will block just as long but at a later time. Either way, you are still blocking your program with a long-running process.
Take the following for example, which is what you ask: take a long-running process and wrap it in a Future, thus making it "asynchronous":
String slowFunction() { ... }
...
String result = await Future(slowFunction);
In normal concurrency, this will put slowFunction in the async queue. The next time the program has some downtime (in between UI draw calls, for example) it will pull that function out of the queue and process it. And thats when it will block for 2-3 seconds while the function executes.
In Dart, though, it works slightly differently. Because slowFunction is not an async function and doesn't await anything, Dart will attempt to run it synchronously anyway, in which case you needn't have bothered wrapping it in a Future in the first place.
You have two options here if you want to break up the operation of your synchronous function. Either you have to break it up into distinct operations that you can await between (which is itself a somewhat complicated process, isn't always possible, and is generally a good source of code smell), or you offload the function to a separate thread altogether, employing parallelism rather than mere concurrency.
Dart is single-threaded, but it can be multi-processed through the use of isolates. (An isolate is Dart's name for a child process and is as close to true multithreading that you can get in Dart.) By wrapping your function in an Isolate, you can run the work on an entirely separate process. That way, if that process blocks for 2-3 seconds, it won't affect the bulk of your app at all.
There is a catch, though. Because isolates are completely different processes, there is no sharing of memory whatsoever. That means any data that the isolate has access to has to be manually passed in through the use of "ports", namely SendPort and ReceivePort. This naturally makes isolate programming a bit of a pain, but in exchange, you won't run into things like your program having race conditions or getting deadlocked. (Because of shared memory problems, at least. Strictly speaking, there are plenty of other ways to get deadlocks and race conditions.)
Using an Isolate works like so:
// main process
void createIsolate() async {
ReceivePort isolateToMain = ReceivePort();
isolateToMain.listen((data) {
// Listen for data passed back to the main process
});
Isolate myIsolateInstance = await Isolate.spawn(myIsolate, isolateToMain.sendPort);
}
// isolate process
void myIsolate(SendPort mainToIsolate) {
final result = slowFunction();
mainToIsolate.send(result);
}

I have a function that might take a few seconds to execute, and it is synchronous. Does:
String slowFunction() { ... }
...
Future<String>(() => slowFunction());
change it to asynchronous?
Simply returning a Future will not make your function asynchronous in the way that you probably want.
When you call an asynchronous function, as much of it as possible will be executed synchronously. That is, the function will execute synchronously until it reaches the first await (i.e., until it returns a Future). Since a Dart isolate is single-threaded, if you want other work to be able to occur concurrently with your long-running operation, slowFunction would internally need to use await (which is syntactic sugar for creating Future.then() callbacks) to allow execution to yield.
Consider the following code:
Future<void> longRunningOperation1() async {
for (var i = 0; i < 100000000; i += 1) {
if (i % 10000000 == 0) {
print('longRunningOperation1: $i');
}
}
}
Future<void> longRunningOperation2() async {
for (var i = 0; i < 100000000; i += 1) {
if (i % 10000000 == 0) {
print('longRunningOperation2: $i');
}
}
}
Future<void> main() async {
await Future.wait([longRunningOperation1(), longRunningOperation2()]);
}
You will see that longRunningOperation1 and longRunningOperation2 never overlap; one always runs to completion before the other one starts. To allow the operations to overlap with minimal changes, you could do:
Future<void> longRunningOperation1() async {
for (var i = 0; i < 100000000; i += 1) {
if (i % 10000000 == 0) {
print('longRunningOperation1: $i');
await Future.delayed(Duration.zero);
}
}
}
Future<void> longRunningOperation2() async {
for (var i = 0; i < 100000000; i += 1) {
if (i % 10000000 == 0) {
print('longRunningOperation2: $i');
await Future.delayed(Duration.zero);
}
}
}

I am using a wrapper to spawn slow operations into a separate Isolate and return aFuture. It also allows to pass the function to run and some arguments as well.
import 'dart:async';
import 'dart:isolate';
/// Example
///
/// ```
/// main() async {
/// String str;
/// str = await runAsync<String, String Function(String)>(sing, ["lalalala"]);
/// print(str);
///
/// str = await runAsync<String, Function>(song);
/// print(str);
/// }
///
/// String sing(String str) => "Singing: " + str;
/// String song() => "lololololo";
/// ```
Future<R> runAsync<R, F>(F func, [List<dynamic> parameters]) async {
final receivePort = ReceivePort();
await Isolate.spawn(asyncRunner, receivePort.sendPort);
// The 'asyncRunner' isolate sends it's SendPort as the first message
final sendPort = await receivePort.first;
final responsePort = ReceivePort();
sendPort.send([responsePort.sendPort, func, parameters ?? []]);
final res = await responsePort.first;
if (res is! R)
return Future.error(res);
else if (res == null) return null;
return res as R;
}
// Isolate entry point
void asyncRunner(SendPort sendPort) async {
// Open the ReceivePort for incoming messages
final port = ReceivePort();
// Notify our creator the port we listen to
sendPort.send(port.sendPort);
final msg = await port.first;
// Execute
final SendPort replyTo = msg[0];
final Function myFunc = msg[1];
final List<dynamic> parameters = msg[2] ?? [];
try {
switch (parameters.length) {
case 0:
replyTo.send(myFunc());
break;
case 1:
replyTo.send(myFunc(parameters[0]));
break;
case 2:
replyTo.send(myFunc(parameters[0], parameters[1]));
break;
case 3:
replyTo.send(myFunc(parameters[0], parameters[1], parameters[2]));
break;
case 4:
replyTo.send(
myFunc(parameters[0], parameters[1], parameters[2], parameters[3]));
break;
case 5:
replyTo.send(myFunc(parameters[0], parameters[1], parameters[2],
parameters[3], parameters[4]));
break;
default:
replyTo.send(Exception("Unsupported argument length"));
}
} catch (err) {
replyTo.send(Exception(err.toString()));
}
// Done
port.close();
Isolate.current.kill();
}
https://github.com/vocdoni/dvote-dart/blob/main/lib/util/asyncify.dart#L16

Related

Break async / wait declaration chain in Dart

I wonder if you can escape the async / wait chain in Dart. AFAIK every time you want to make a sync call to an async function you must use await but this forces you to make the function which holds the instruction to become async too.
Here is an example:
Future<String> helloAsync() async{
return Future.delayed(const Duration(seconds: 2), ()=>'Hello');
}
void helloSync(){
//call helloAsync synchronously (somehow without making this function async)
//print returned value
}
void main(){
helloSync();
}
In here if I want to make a sync call to helloAsync() inside the HellowSync() I must add await like:
void helloSync(){
String s = await helloAsync();
print(s);
}
but this will not work until I add the async keyword for it - and if I do this means that I have to do it for main() function too.
Is there a way to break this chain of async / await declaration?
(In the initial example this would mean no async declaration for main function)
You could use .then() instead. You can't return the result of a Future from a synchronous function, but since your helloSync() returns void anyway, .then() might be what you are looking for.
void helloSync(){
helloAsync().then((result) => print(result));
}

Async/Await in Dart

I'm making a Flutter app that using asynchronous a lot but it not working like how I understand about it. So I have some question about async and await in dart. Here is an example:
Future<int> someFunction() async {
int count = 0;
for (int i=0; i< 1000000000;i ++) {
count+= i;
}
print("done");
return count;
}
Future<void> test2() async {
print("begin");
var a = await someFunction();
print('end');
}
void _incrementCounter() {
print("above");
test2();
print("below");
}
test2() function will take a lot of time to done. right? So what i want is when test2 keep his work running until done, everything will keep running and not wait for test2().
When i run the function _incrementCounter(), it show the result:
above begin done below end
The problem is it didn't show "below" right away but it wait until someFunction() done.
This is result i want:
above begin below done end
This is the expected behavior since this change in Dart 2.0 which can be found in the changelog:
(Breaking) Functions marked async now run synchronously until the first await statement. Previously, they would return to the event loop once at the top of the function body before any code runs (issue 30345).
Before I give the solution I want to note you that async code are not running in another thread so the concept of:
keep his work running until done, everything will keep running and not
wait for test2()
Is fine but at some point your application are going to wait for test2() to finish since it is spawned as a task on the job queue where it will not leave other jobs to run before it is done. If you want the experience of no slowdown you want to either split the job into multiple smaller jobs or spawn an isolate (which are running in another thread) to run the calculation and later return the result.
Here is the solution go get your example to work:
Future<int> someFunction() async {
int count = 0;
for (int i=0; i< 1000000000;i ++) {
count+= i;
}
print("done");
return count;
}
Future<void> test2() async {
print("begin");
var a = await Future.microtask(someFunction);
print('end');
}
void _incrementCounter() {
print("above");
test2();
print("below");
}
main() {
_incrementCounter();
}
By using the Future.microtask constructor we are scheduling the someFunction() to be running as another task. This makes it so the "await" are going to wait since it will be the first true instance of an async call.

What's the difference between async and async* in Dart?

I am making an application using flutter framework .
During this I came across with the keywords in Dart async and async*.
Can anybody tell me what's the difference between them?
Short answer
async gives you a Future
async* gives you a Stream.
async
You add the async keyword to a function that does some work that might take a long time. It returns the result wrapped in a Future.
Future<int> doSomeLongTask() async {
await Future.delayed(const Duration(seconds: 1));
return 42;
}
You can get that result by awaiting the Future:
main() async {
int result = await doSomeLongTask();
print(result); // prints '42' after waiting 1 second
}
async*
You add the async* keyword to make a function that returns a bunch of future values one at a time. The results are wrapped in a Stream.
Stream<int> countForOneMinute() async* {
for (int i = 1; i <= 60; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}
The technical term for this is asynchronous generator function. You use yield to return a value instead of return because you aren't leaving the function.
You can use await for to wait for each value emitted by the Stream.
main() async {
await for (int i in countForOneMinute()) {
print(i); // prints 1 to 60, one integer per second
}
}
Going on
Watch these videos to learn more, especially the one on Generators:
Isolates and Event Loops
Futures
Streams
async / await
Generators
Marking a function as async or async* allows it to use the async/await for a Future.
The difference between both is that async* will always return a Stream and offer some syntactical sugar to emit a value through the yield keyword.
We can therefore do the following:
Stream<int> foo() async* {
for (int i = 0; i < 42; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}
This function emits a value every second, which increments every time.
Solution, Origins and Insights
This answer includes simplified and easy to understand examples
async
The async computation cannot provide a result immediately when it is started because the program may need to wait for an external response like:
Reading a file
Querying a database
Fetching data from an API
Instead of blocking all computation until the result is available, the asynchronous computation immediately returns a Future object which will eventually "complete" with the result.
Example (This type of async call can be used only without returning a response):
void main() async {
// The next line awaits 5 seconds
await Future.delayed(Duration(seconds: 5));
// Pseudo API call that takes some time
await fetchStocks();
}
Future
A Future represents a computation that doesn’t complete immediately. Whereas a normal function returns the result, an asynchronous function returns a Future, which will
eventually contain the result. The Future will tell you when the result is ready.
Future is appended when the async function returns a value
Represents the result of a single computation (in contrast to a Stream)
Example:
Future<String> fetchUserOrder() =>
// Imagine that this function is more complex and slow.
Future.delayed(
const Duration(seconds: 2),
() => 'Large Latte',
);
void main(List<String> arguments) async {
var order = await fetchUserOrder();
// App awaits 2 seconds
print('Your $order is ready');
}
Stream
A source of asynchronous data events.
A Stream provides a way to receive a sequence of events. Each event is either a data event, also called an element of the stream.
Stream is a sequence of results
From stream you get notified for results (stream elements)
async* (streams)
async* is an asynchronous generator that returns a Stream object. Made to create streams.
An example of using a stream and async*:
// Creating a new stream with async*
// Each iteration, this stream yields a number
Stream<int> createNumberStream(int number) async* {
for (int i = 1; i <= number; i++) {
yield i;
}
}
void main(List<String> arguments) {
// Calling the stream generation
var stream = createNumberStream(5);
// Listening to Stream yielding each number
stream.listen((s) => print(s));
}
Result:
1
2
3
4
5
Bonus: Transforming an Existing Stream
If you already have a stream, you can transform it to a new stream based on the original stream’s events.
Example (same code as before but with a twist):
Stream<int> createNumberStream(int number) async* {
for (int i = 1; i <= number; i++) {
yield i;
}
}
// This part is taking a previous stream through itself and outputs updated values
// This code multiplies each number from the stream
Stream<int> createNumberDoubling(Stream<int> chunk) async* {
await for (final number in chunk) {
yield number*2;
}
}
void main(List<String> arguments) {
// Here we are Transforming the first stream through createNumberDoubling stream generator
var stream = createNumberDoubling(createNumberStream(5));
stream.listen((s) => print(s));
}
Result:
2
4
6
8
10
Solution
The async and async* are close relatives, they are even from the same library dart:async
The async represent a Future and a one-time exchange while the async* represents a Stream, a stream of multiple events
Async functions execute synchronously until they reach the await keyword. Therefore, all synchronous code within an async function body executes immediately.
Future<int> foo() async {
await Future.delayed(Duration(seconds: 1));
return 0;
}
Async* is used to create a function that returns a bunch of future values one at a time. Each result is wrapped in a Stream.
Stream<int> foo() async* {
for (var i = 0; i < 10; i++) {
await Future.delayed(Duration(seconds: 1));
yield i;
}
}
async* will always return a Stream
Stream<int> mainStream(int value) async* {
for (int i = 1; i <= value; i++) {
yield i;
}
}
async returns the result wrapped in the Future. So it might take longer time. See the below example:
void main() async {
// The next line awaits 10 seconds
await Future.delayed(Duration(seconds: 10));
}

Controlling while loops in Dart

I am stuck trying to do something simple. I want to be able to count upwards until I click on the screen at which point i want the counting to stop. In reality the code itself will be carrying out complex AI calculations for a game, but first I want to understand how to control the while loop. In android this would be trivial.
Here is what my code looks like
bool OK = true;
main() async{
html.querySelector('#content').onMouseUp.listen((e){
OK = false;
});
await for(int i in naturals){
print(i);
await sleep();
}
}
Stream get naturals async* {
int k = 0; while (OK) { yield await k++; }
}
Future sleep() {
return new Future.delayed(const Duration(milliseconds: 1), () => "1");
}
I put the sleep() method in as a way to ensure that control is passed to the event loop.
Is it possible to control a while loop without the sleep() method?
update
Just enqueue to allow the event queue to process outstanding events without additional delay use
Future sleep() {
return new Future.delayed(Duration.ZERO);
}
original
JavaScript and therefore can't Dart have threads in the browser and you need to pass control back to the event queue to allow it to process other events like you do with sleep().
Another approach would be to use webworkers and run the heavy calculation in a background task (webworker) to keep the UI thread free to process user events. This way even additional CPU cores can be utilized which isn't possible when all code runs in the browsers UI thread.
I don't know how to create webworkers in Dart though except with Angular2 Dart Web workers in Angular 2 Dart where Angular does the initialization.
I don't think it's too difficult but I don't know any docs.
To provide a more general answer - instead of a loop, you want to schedule a sequence of future tasks that each executes one iteration or step of your AI code (or whatever background process you want to have running).
You can have the step task recursively schedule itself:
dynamic doSomething(_) {
print('Doing something ...');
if(!stop) {
new Future.delayed(delay, (){}).then(doSomething);
}
return null;
}
main() async {
doSomething(null);
}
Although I don't recommend doing this. It's awkward to control - the step code has to check a flag variable to see if it should continue or stop and it's free running.
Alternatively you could use a Timer:
void doSomething(Timer timer) {
print('Doing something ...');
}
main() async {
new Timer.periodic(delay, doSomething);
}
This is throttled at a constant rate and has a uniform time step, and is easier to stop (call cancel() on the timer).
Another approach might be to synchronize with the browser's draw refresh cycle by requesting future animation frames:
import 'dart:html';
doSomething(num delta) {
print('Doing something ...');
window.animationFrame.then(doSomething);
}
void main() {
window.animationFrame.then(doSomething);
}
Time steps are not constant but you get the time delta. The advantage of this approach is that animation frame futures will not be scheduled if the browser window is hidden.
See How do I drive an animation loop at 60fps with Dart?
Those are very simple examples. Setting up proper background processes for physics simulation and AI in web games is actually surprisingly (to me at least) non-trivial. Here are two resources I found helpful.
http://gameprogrammingpatterns.com/ - a nice free online book of game programming patterns. http://gameprogrammingpatterns.com/game-loop.html - chapter on game loops.
http://gafferongames.com/game-physics/fix-your-timestep/ - part of a sequence of articles on physics simulation in games.
Following all suggestions this is the code I have ended up with, putting the heavy lifting in a worker, with controllable stopping and starting. I have used simple counting to get this working, but this is being replaced with my complex AI game calculations.
This uses time slices of 100ms within a worker isolate, which can only be interrupted after it has finished the batch. This allows me complete freedom to have complex animations on the screen while all this hard work is being done.
import 'dart:html' as html;
import 'dart:isolate';
import 'dart:math';
SendPort worker;
bool working = false;
main() async{
await startWorker();
html.querySelector('#stage').onMouseUp.listen((e){
if(working)worker.send('stop');
else worker.send('go');
});
}
startWorker() async{
var response = new ReceivePort();
await Isolate.spawnUri(Uri.parse("worker.dart"), null ,response.sendPort)
.then((_) => response.listen((msg){
if(msg is SendPort) {
worker = msg;
}
else {
messageReceived(msg);
}
}));
}
messageReceived(String message){
switch (message){
case 'working': working = true;
break;
case 'idle': working = false;
break;
default : print(message);
break;
}
}
worker.dart
import 'dart:isolate';
import 'dart:async';
SendPort replyTo;
bool OK = false;
const timeSlice = 100;
main(List<String> args, SendPort reply) async{
var response = new ReceivePort();
reply.send(response.sendPort);
replyTo = reply;
response.listen((msg) => messageReceived(msg));
replyTo.send("Hello from worker. Click to start and stop me");
}
messageReceived(String message){
switch(message){
case 'stop':
replyTo.send('idle');
OK = false;
break;
case 'go':
replyTo.send('working');
go();
break;
}
}
go()async {
OK = true;
int i = 0;
batchJob(){
int startTime = new DateTime.now().millisecondsSinceEpoch;
int elapsed = 0;
while(elapsed < timeSlice){
i ++; // the central routine
elapsed = new DateTime.now().millisecondsSinceEpoch - startTime;
}
}
while(OK){
batchJob();
replyTo.send(i.toString());
await new Future.delayed(const Duration(milliseconds: 0), () {});
}
}

What are the pros and cons of async, when to and when not to use it and what other alternatives to callback are there?

callbacks or asynchronous methods or other options
A solution to the callback plague is "await" and "async" or more specifacally 'dart:async' library.
Now, what is the cost of asynchrony?
When should we not use them?
What are the other alternatives?
The below is a badly coded non-polymer custom element that acts like a messageBox in desktop environment. It gives me less braces and parenthesis-es but requires the caller to be also async or use "show().then((v){print(v);});" pattern. Should I avoid the pattern like this?
Is callback better? Or there is an even smarter way?
Polling version
import 'dart:html';
import 'dart:async';
void init(){
document.registerElement('list-modal',ListModal);
}
class ListModal extends HtmlElement{
ListModal.created():super.created();
String _modal_returns="";
void set modal_returns(String v){
///use the modal_returns setter to
///implement a custom behaviour for
///the return value of the show method
///within the callback you can pass on calling append .
_modal_returns=v;
}
factory ListModal(){
var e = new Element.tag('list-modal');
e.style..backgroundColor="olive"
..position="absolute"
..margin="auto"
..top="50%"
..verticalAlign="middle";
var close_b = new DivElement();
close_b.text = "X";
close_b.style..right="0"
..top="0"
..margin="0"
..verticalAlign="none"
..backgroundColor="blue"
..position="absolute";
close_b.onClick.listen((_){
e.hide();
});
e.append(close_b,(_)=>e.hide());
e.hide();
return e;
}
#override
ListModal append(
HtmlElement e,
[Function clickHandler=null]
){
super.append(e);
if(clickHandler!=null) {
e.onClick.listen(clickHandler);
}else{
e.onClick.listen((_){
this.hide();
_modal_returns = e.text;
});
}
return this;
}
Future<String> show() async{
_modal_returns = '';
this.hidden=false;
await wait_for_input();
print(_modal_returns);
return _modal_returns;
}
wait_for_input() async{
while(_modal_returns=="" && !this.hidden){
await delay();
}
}
void hide(){
this.hidden=true;
}
Future delay() async{
return new Future.delayed(
new Duration(milliseconds: 100));
}
}
Non-polling version
In response to Günter Zöchbauer's wisdom(avoid polling), posting a version that uses a completer. Thanks you as always Günter Zöchbauer:
import 'dart:html';
import 'dart:async';
void init(){
document.registerElement('list-modal',ListModal);
}
class ListModal extends HtmlElement{
ListModal.created():super.created();
String _modal_returns="";
Completer _completer;
void set modal_returns(String v){
///use the modal_returns setter to
///implement a custom behaviour for
///the return value of the show method.
///Use this setter within the callback for
///append. Always call hide() after
///setting modal_returns.
_modal_returns=v;
}
factory ListModal(){
var e = new Element.tag('list-modal');
e.style..backgroundColor="olive"
..position="absolute"
..margin="auto"
..top="50%"
..verticalAlign="middle";
var close_b = new DivElement();
close_b.text = "X";
close_b.style..right="0"
..top="0"
..margin="0"
..verticalAlign="none"
..backgroundColor="blue"
..position="absolute";
close_b.onClick.listen((_){
e.hide();
});
e.append(close_b,(_){e.hide();});
e.hide();
return e;
}
#override
ListModal append(
HtmlElement e,
[Function clickHandler=null]
){
super.append(e);
if(clickHandler!=null) {
e.onClick.listen(clickHandler);
}else{
e.onClick.listen((_){
_modal_returns = e.text;
this.hide();
});
}
return this;
}
Future<String> show() async{
_modal_returns = '';
_completer = new Completer();
this.hidden=false;
return _completer.future;
}
void hide(){
hidden=true;
_completer?.complete(_modal_returns);
_completer=null;
}
}
Usually there is no question whether async should be used or not. Usually one would try to avoid it. As soon as you call an async API your code goes async without a possibility to choose if you want that or not.
There are situations where async execution is intentionally made async. For example to split up large computation in smaller chunks to not starve the event queue from being processed.
On the server side there are several API functions that allow to choose between sync and async versions. There was an extensive discussion about when to use which. I'll look it up and add the link.
The disadvantages of using async / await instead of .then() should be minimal.
minimal Dart SDK version with async / await support is 1.9.1
the VM needs to do some additional rewriting before the code is executed the first time, but this is usually neglectable.
Your code seems to do polling.
wait_for_input() async {
while(_modal_returns=="" && !this.hidden){
await delay();
}
}
This should be avoided if possible.
It would be better to let the modal manage its hidden state itself (by adding a hide() method for example), then it doesn't have to poll whether it was hidden from the outside.

Resources