Convert an async function to a sync function.

4,899 views
Skip to first unread message

George Moschovitis

unread,
Jul 12, 2014, 8:41:54 AM7/12/14
to mi...@dartlang.org
Is there a way to block until an async function completes it's future and return the value of the future?
As an example I would like to use an async function within a callback to replaceAllMapped:

Future<String> myAsyncFunction(String input) {
  ..
}

content.replaceAllMapped(MY_REGEX, (match) {
  return $make_sync(myAsyncFunction(match.group(0)));
});

What would $make_sync look like? Future.sync() doesn't look helpful here.


Lasse R.H. Nielsen

unread,
Jul 12, 2014, 8:55:32 AM7/12/14
to mi...@dartlang.org
On Sat, Jul 12, 2014 at 2:41 PM, George Moschovitis <george.mo...@gmail.com> wrote:
Is there a way to block until an async function completes it's future and return the value of the future?

No.
"Blocking" in a synchronous function would mean that nothing else in the isolate gets to run, so the future would not complete.
If you allow other code to run while "blocking", then you are not really a synchronous function.

What you are asking for requires some kind of co-routines, where you can suspend normal code, and return to it later. Dart doesn't have that functionality, it uses asynchronous functions instead.

 As an example I would like to use an async function within a callback to replaceAllMapped:

Future<String> myAsyncFunction(String input) {
  ..
}

content.replaceAllMapped(MY_REGEX, (match) {
  return $make_sync(myAsyncFunction(match.group(0)));
});

What would $make_sync look like? Future.sync() doesn't look helpful here.


It does not exist, and currently can't exist.

It is a good argument for adding coroutines though - replaceAllMapped is not a function that we would make an async version of, but that restricts its callback to being synchronous.
If we had co-routines, we could "pause" inside any function, and still come back and continue, without it affecting the surrounding functions.

/L
--
Lasse R.H. Nielsen - l...@google.com  
'Faith without judgement merely degrades the spirit divine'
Google Denmark ApS - Frederiksborggade 20B, 1 sal - 1360 København K - Denmark - CVR nr. 28 86 69 84

George Moschovitis

unread,
Jul 12, 2014, 12:27:27 PM7/12/14
to mi...@dartlang.org

What you are asking for requires some kind of co-routines, where you can suspend normal code, and return to it later. Dart doesn't have that functionality, it uses asynchronous functions instead.

I am wondering how File.reasAsStringSync() is implemented.
 
 As an example I would like to use an async function within a callback to replaceAllMapped:

Future<String> myAsyncFunction(String input) {
  ..
}

content.replaceAllMapped(MY_REGEX, (match) {
  return $make_sync(myAsyncFunction(match.group(0)));
});

What would $make_sync look like? Future.sync() doesn't look helpful here.


It does not exist, and currently can't exist.

It is a good argument for adding coroutines though - replaceAllMapped is not a function that we would make an async version of, but that restricts its callback to being synchronous.

non-orthogonal, to say the least...

Lasse R.H. Nielsen

unread,
Jul 12, 2014, 1:12:01 PM7/12/14
to mi...@dartlang.org
On Sat, Jul 12, 2014 at 6:27 PM, George Moschovitis <george.mo...@gmail.com> wrote:

What you are asking for requires some kind of co-routines, where you can suspend normal code, and return to it later. Dart doesn't have that functionality, it uses asynchronous functions instead.

I am wondering how File.reasAsStringSync() is implemented.

Most likely by doing a blocking OS call. Nothing else happens in the isolate until it returns, but the OS is free to fetch a file and fill a buffer.
 
It is a good argument for adding coroutines though - replaceAllMapped is not a function that we would make an async version of, but that restricts its callback to being synchronous.

non-orthogonal, to say the least..

Absolutely. Being asynchronous is infectious. 

/L

Alex Tatumizer

unread,
Jul 12, 2014, 1:18:50 PM7/12/14
to mi...@dartlang.org
Problem would go away if auto-await for Futures was implemented by compiler/runtime by default..  Otherwise, it's a puzzle. Introducing more keywords and more concepts doesn't make the puzzle disappear.


George Moschovitis

unread,
Jul 12, 2014, 1:21:23 PM7/12/14
to mi...@dartlang.org
On Saturday, July 12, 2014 8:18:50 PM UTC+3, Alex Tatumizer wrote:
Problem would go away if auto-await for Futures was implemented by compiler/runtime by default..  Otherwise, it's a puzzle. Introducing more keywords and more concepts doesn't make the puzzle disappear.

If I understand LRHN correctly, (auto-)await would not help in this use-case.

 

Alex Tatumizer

unread,
Jul 12, 2014, 1:31:13 PM7/12/14
to mi...@dartlang.org
Why not? With auto-await, every function will turn into "async" function by default, so "infection" takes place upfront, for everybody.
Certainly, it's too late at this stage, but now i vaguely remember, Gilad once mentioned (in the very beginning of dart project) that he considered something like auto-await (though he used different terms).
I might be wrong though.

George Moschovitis

unread,
Jul 12, 2014, 2:32:18 PM7/12/14
to mi...@dartlang.org
Well, it seems I don't understand what you mean with auto-await, can you point me to a relevant thread or something?

Alex Tatumizer

unread,
Jul 12, 2014, 2:46:56 PM7/12/14
to mi...@dartlang.org
No thread to speak of. the idea is simple: when any Future is returned, system INSERTS await.
And every function is async by default.
Default can be overridden, e.g. you can write
var fut= go functionReturningFuture();

It's not practical to implement in dart at this point: type annotations for return values are erased - but it's not that there's a law requiring them to be erased.
No one from dart team ever discussed the idea, it's my own improvisation, maybe it has complications I'm not aware about. 


Peter Ahé

unread,
Jul 12, 2014, 4:12:27 PM7/12/14
to mi...@dartlang.org
I'm not sure it would be feasible to implement "auto-await" in JavaScript. In principle, it sounds a lot like what you'd need to support non-local returns.

In both cases, one implementation strategy is for the compiler to insert a check after each dynamic call it cannot analyze.

For non-local returns, the check would be if the current function must be returned from early.

For "auto-await", the check would be to see if the rest of the code in the current function should be evaluated now or returned as a Future. Unfortunately, this would mean that most expressions have to be turned into a continuation function, and I assume this would lead to an enormous amount of generated code, and it'll probably be slow as well.

But it lacks another property of what we currently have. Because Dart isn't multi-threaded, you don't need to worry about race conditions in synchronous code.

With your suggestion, everything would become asynchronous, and we'd have to add some way to protect against that, and then you'd quickly have deadlocks and race conditions, just like you know from multi-threaded programs. But without the benefits of multiple threads.

Cheers,
Peter
--
For other discussions, see https://groups.google.com/a/dartlang.org/
 
For HOWTO questions, visit http://stackoverflow.com/tags/dart
 
To file a bug report or feature request, go to http://www.dartbug.com/new

To unsubscribe from this group and stop receiving emails from it, send an email to misc+uns...@dartlang.org.

Alex Tatumizer

unread,
Jul 12, 2014, 6:25:45 PM7/12/14
to mi...@dartlang.org
Peter, I think you make it more complicated than it should be.
All Futures in dart are created by some function calls.
Consider a function f:
T f() {
  var v1=op1();
  var v2=op2();
  etc.
Let's ignore performance issues for now.
the issue under discussion is: should compiler automatically insert awaits for each call, or not? If yes, function is transformed into:
T f() {
  var v1= await op1();
  var v2=await op2();
  etc.
I think it all depends on whether function f is declared as async or not.
If yes, every Future inside it should be auto-awaited, because programmer will insert "await" manually anyway.
If it's not declared async, everything remains as is it now.

(Of course, one can use expressions like "go op1()", or equivalent, in which case not auto-await will take place)

It looks too simple, probably I'm missing something...

Robert Åkerblom-Andersson

unread,
Jul 12, 2014, 7:26:51 PM7/12/14
to mi...@dartlang.org
Hi George,

Just as Lasse said, I have also found (by lots of testing) that this is not possible. 

However, for some use cases, if you are on the server, I think Vane's middleware system can help you out. It is not exactly the type of co-routines Lasse speaks of but Vane's middleware system/runtime does provide some abstraction and flow control build on normal dart Futures. The middleware handlers can be thought of as a type of co-routines and can be setup to run either synchronously or asynchronously or a combination of the two.

This is a working example that waits on a message and then prints it, I added both print and writeln statements to show both in the console output and in the html output.

library main;

import 'dart:async';
import 'package:vane/vane.dart';

class Test extends Vane {
  var pipeline = [ProcessData1, This];

  @Route("/")
  Future test([String name = ""]) {
    // Print start message for easier understanding of flow
    print("Start of test, pipeline index: ${pIndex}");
    writeln("Start of test, pipeline index: ${pIndex}");

    // Receive value on the tube
    var value = tube.receive();

    // Print value
    print("Our msg: ${value}");
    writeln("Our msg: ${value}");

    return close();
  }
}

class ProcessData1 extends Vane {
  Future main() {
    print("Start of ProcessData1, pipeline index: ${pIndex}");
    writeln("Start of ProcessData1, pipeline index: ${pIndex}");

    // replaceAllMapped() example from dartlang.org
    pigLatin(String words) => words.replaceAllMapped(
        new RegExp(r'\b(\w*?)([aeiou]\w*)', caseSensitive: false),
        (Match m) => "${m[2]}${m[1]}${m[1].isEmpty ? 'way' : 'ay'}");

    // Do something async, we use a Timer here to simulate any asynchronous operation
    new Timer(new Duration(seconds: 3), () {
      print("Inside ProcessData1 Timer, doing async work...");
      writeln("Inside ProcessData1 Timer, doing async work...");

      var msg = pigLatin('I have a secret now!');

      tube.send(msg);
      next();
    });

    print("End of ProcessData1, pipeline index: ${pIndex}");
    writeln("End of ProcessData1, pipeline index: ${pIndex}");

    return end;
  }
}

void main() => serve();

Just add "vane" to you pubspec.yaml, run the program and then go to: http://localhost:9090/

Essentially you can more or less get the same behavior with a couple of ".then()", but when you start adding more than one of them it quite quickly becomes like this:

And with each and then, you also get more indentation, and more indentation etc...

More example of how to use Vane middleware handlers can be found here: http://www.dartvoid.com/vane/

Regards, Robert 

Günter Zöchbauer

unread,
Jul 14, 2014, 12:32:23 PM7/14/14
to mi...@dartlang.org


On Sunday, July 13, 2014 1:26:51 AM UTC+2, Robert Åkerblom-Andersson wrote:
Hi George,

Just as Lasse said, I have also found (by lots of testing) that this is not possible. 

...
 

Yeah, this is a bit to much `and then` :D
And with each and then, you also get more indentation, and more indentation etc...

More example of how to use Vane middleware handlers can be found here: http://www.dartvoid.com/vane/

Regards, Robert 

to reduce unlimited indentation. 
Reply all
Reply to author
Forward
0 new messages