synchronously waiting for a Promise

3,674 views
Skip to first unread message

Ian Preston

unread,
Aug 31, 2016, 5:00:00 PM8/31/16
to GWT Users
Hi guys,

I'm trying to wrap a JS function which returns a Promise with a synchronous function in Java. Something like CompletableFuture.get(). I understand that CompletableFuture isn't emulated yet. 

Is there any other way to achieve this? The alternative of propagating callbacks/promises all the way up my Java stack is not really an option. 

Many thanks for any suggestions,
Ian

Ian Preston

unread,
Aug 31, 2016, 5:58:51 PM8/31/16
to GWT Users
One idea, which would be awesome from a user perspective, is the following:

Emulate CompletableFuture with native Promises. Then if the synchronous cf.get() call is used, then translate that to await, and make the function it is in async (in JS land). This would automatically change the signature to a CompletableFuture, and then propagate this up to all callers. If it makes it all the way to a function exposed with JsInterop, then that function changes signature like the rest of them to return a promise. The consumer of this function is obviously in JS and so can handle the promise fine. This would equate to translating the synchronous java calls which wait on a CompletableFuture (Promise) to return promises in JS. This wouldn't require any changes to the Java code - it could continue in its synchronous style, and not disrupt JVM based users of the same code. 

Thoughts?

Jens

unread,
Aug 31, 2016, 9:00:46 PM8/31/16
to GWT Users
There is work being done to emulate CompletableFuture but the synchronous methods of Future will not be supported. There are a number of reasons why your suggestion using JS async/await won't happen anytime soon.

You should make yourself comfortable with the idea of having an async API on the client.

-- J.


Thomas Broyer

unread,
Sep 1, 2016, 5:22:08 AM9/1/16
to GWT Users
'await' is not synchronous, which means things can happen while awaiting, which could mutate state you'd rely on being immutable because you 'synchronized' it (in other words: how would your proposal work with the 'synchronized' keyword? GWT currently simply ignores 'synchronized' because JS is single-threaded anyway; now what if you call CompletableFuture#get() within a 'synchronized' function, possibly several levels deep in the call-stack?).

There are good reasons why only async functions can call async functions, and only async functions can use the await keyword: explicit vs. implicit.

Ian Preston

unread,
Sep 1, 2016, 5:53:45 AM9/1/16
to GWT Users
Interesting question, Thomas. I believe it should still work. Consider the following: 

1. A 'thread' comes into a method and obtains a lock on A.
2. whilst holding the lock, it makes an async call to CompletableFuture.get()
3. Somewhere down the call stack some state which is guarded by the lock on A is modified
On the JVM, either that lock is not re-entrant, in which case it is a deadlock, or it is re-entrant and modifying the state is fine. We only have one thread in JS so it is fine. 

Correct me if I'm wrong, but I believe this means that GWT could continue to ignore synchronization, and get the same results assuming non deadlocking programs, which can't be translated without a full emulator like doppio anyway. 

Thomas Broyer

unread,
Sep 1, 2016, 8:59:45 AM9/1/16
to GWT Users
Consider the following code:

class Foo {
  String bar;

  synchronized String getBar() {
    if (bar == null) { // OK, we all know this is bad in the Java world, just bear with me for this sample code
      Future<String> realBar = …; // make a call that returns a CompletableFuture<String>
      bar = realBar.get();
    }
    return bar;
  }
}

You're proposing that it's somehow translated more or less to:

var _symbol$bar = Symbol("Foo.bar");
class Foo {
  async getBar() { // Note: transformed to 'async', no 'synchronized'
    if (this[_symbol$bar] == null) {
      var realBar = …;
      this[_symbol$bar] = await realBar.get(); // transformed to await
    }
    return this[_symbol$bar];
  }
}

Now imagine that while await⋅ing realBar.get(), getBar() is called again, on the same Foo instance (triggered by an event; for example, getBar() is called from a click handler, and the … returning the CompletableFuture<String> fetches some resource through HTTP).
Looking at the original Java code, you'd expect that the second call is blocked until the first one terminates and releases the lock on the Foo object, so the first call would set the bar field and the second call would skip the 'if' branch.
In JS though, the second call would *enter* the 'if' branch and make a second call; then, when each 'realBar' is completed, the private (through Symbol) property is set: twice; and if the call that returned a CompletableFuture<String> is stateful, that means the state has been modified twice.
What kind of JS would you produce that'd prevent this from happening?

I wouldn't trade "emulating CompletableFuture#get" for "bloated JS and a much more complex compiler". The Pareto rule tells us that you should just embrace asynchrony and live with only the non-blocking API (CompletionStage basically, plus getNow() and a few others).

Ian Preston

unread,
Sep 1, 2016, 10:01:56 AM9/1/16
to GWT Users
Cool example. You are right. 

We are migrating our code to asynchronous. :-)

Steve Hannah

unread,
Sep 1, 2016, 10:09:56 AM9/1/16
to google-we...@googlegroups.com
If you require synchronous code you may want to check out TeaVm.  It supports full Java thread semantics in the browser. 
--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsub...@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.


--
Steve Hannah
Web Lite Solutions Corp.

Jens

unread,
Sep 1, 2016, 10:10:12 AM9/1/16
to GWT Users

var _symbol$bar = Symbol("Foo.bar");
class Foo {
  async getBar() { // Note: transformed to 'async', no 'synchronized'
    if (this[_symbol$bar] == null) {
      var realBar = …;
      this[_symbol$bar] = await realBar.get(); // transformed to await
    }
    return this[_symbol$bar];
  }
}

I think this isn't even correct, because "async getBar()" does return a promise simply because its marked async. Given that you can only call await inside async functions I think its nearly impossible to map that to a synchronous Java function while keeping the correct return type or am I missing something?


-- J.

Thomas Broyer

unread,
Sep 1, 2016, 10:19:02 AM9/1/16
to GWT Users
That was part of Ian's proposal (emphasis mine):

“If it makes it all the way to a function exposed with JsInterop, then that function changes signature like the rest of them to return a promise. The consumer of this function is obviously in JS and so can handle the promise fine. This would equate to translating the synchronous java calls which wait on a CompletableFuture (Promise) to return promises in JS.” 

Jens

unread,
Sep 1, 2016, 10:20:02 AM9/1/16
to GWT Users

I think this isn't even correct, because "async getBar()" does return a promise simply because its marked async. Given that you can only call await inside async functions I think its nearly impossible to map that to a synchronous Java function while keeping the correct return type or am I missing something?

To be clear:

async getBar() {
  var str = await getSomeStringAsync();
  return str;
}

must map to Promise<String> / CompletableFuture<String> getBar() in Java just to get the return type correct. But that defeats the purpose of trying to make getBar() look synchronous in Java. And you can not internally unwrap that Promise (e.g. through getBar() implemented as getBarImpl().then(...)) synchronously.

So I think this can't really work.

-- J.

Thomas Broyer

unread,
Sep 1, 2016, 10:20:55 AM9/1/16
to GWT Users
…and yes, that'd mean that one small change in one class could change the exposed API from/to a Promise "at the other end" of your project. Good luck tracking those changes and never introduce a bug because of them ;-)

Ian Preston

unread,
Sep 1, 2016, 10:21:05 AM9/1/16
to GWT Users
Jens, my suggestion was to propagate the asyncs up the call stack to all callers of the function, automatically changing each one to return a promise, and use await on the calls to the async functions, which would work fine except for losing the atomic synchonization of the functions effects in JS, relative to other events

Jens

unread,
Sep 1, 2016, 11:27:37 AM9/1/16
to GWT Users

Jens, my suggestion was to propagate the asyncs up the call stack to all callers of the function, automatically changing each one to return a promise, and use await on the calls to the async functions, which would work fine except for losing the atomic synchonization of the functions effects in JS, relative to other events
 
I see, yeah ok that might work however I think it would instantly break your code assumptions because of state modifications.

The reason is that the code that schedules an asynchronous task is synchronous in itself. So if you have a long call stack and at the bottom you schedule an XMLHttpRequest then everything is still executed synchronously, only the result of the request comes in asynchronously and only the code that runs when retrieving the result executes asynchronously.

If you now rewrite that to async / await and propagate it through the call stack then you have lots of await keywords in your call stack now and at all those locations suddenly other code can execute. So when looking at your Java code from a higher level you can never be sure if two lines of code within the same method body execute synchronously or not.

-- J.

Ignacio Baca Moreno-Torres

unread,
Sep 2, 2016, 2:35:11 AM9/2/16
to GWT Users
Don't forget to check out this https://github.com/intendia-oss/rxjava-gwt ;)

You'll probably get much better results asynchronously, at the end await/async is just and advanced (and maybe obscure) syntactic sugar over callbacks (I see that way, maybe not), which do not allow the versatility obtained with other asynchronous monads like Promise or Observable. Async forces a sequential execution (which makes sense because is to make your imperative synchronous code asynchronous, so the original sequence should be maintained), but with Observable you have all the control in a descriptive way.
Reply all
Reply to author
Forward
0 new messages