Reputation: 413
A couple of the Play Framework devs have said "Never, ever call get() on a Promise because it can lead to deadlocks." Ok.
Say I have legacy code that expects Bar
(not F.Promise<Bar>
) and calls apply(Foo foo)
below to get it. Within apply
, I'd like to do some concurrent webservice calls, wait for the responses and then use them to make a Bar
. How can I do that without calling futureBar.get()
, again assuming that returning F.Promise<Bar>
is not an option?
public class Func implements F.Function<Foo,Bar> {
@Override
public Bar apply(Foo foo) throws Throwable {
F.Promise<WS.Response> response1 = WS.url("http://google.com").get();
F.Promise<WS.Response> response2 = WS.url("http://yahoo.com").get();
F.Promise<List<WS.Response>> responses = F.Promise.sequence(response1, response2);
F.Promise<Bar> futureBar = responses.map(new F.Function<List<WS.Response>, Bar>() {
@Override
public Bar apply(List<WS.Response> o) throws Throwable {
//some code;
return bar;
}
});
//How can I return Bar without calling get?
}
}
Upvotes: 3
Views: 1673
Reputation: 7257
Instead of returning a Bar
, return a F.Promise<Bar>
.
public class Func implements F.Function<Foo,F.Promise<Bar>> {
@Override
public F.Promise<Bar> apply(Foo foo) throws Throwable {
F.Promise<WS.Response> response1 = WS.url("http://google.com").get();
F.Promise<WS.Response> response2 = WS.url("http://yahoo.com").get();
F.Promise<List<WS.Response>> responses = F.Promise.sequence(response1, response2);
F.Promise<Bar> futureBar = responses.map(new F.Function<List<WS.Response>, Bar>() {
@Override
public Bar apply(List<WS.Response> o) throws Throwable {
//some code;
return bar;
}
});
return futureBar;
}
}
From there on, you operate on Bar
within the Promise
, using map
, flatMap
and filter
as described in the Javadoc: http://www.playframework.com/documentation/2.2.x/api/java/play/libs/F.Promise.html
When working asynchronously, you get used to always dealing with your code in the context of a future, this is how you keep your code asynchronous.
In Scala, you would do this:
def apply(foo: Foo): Future[Bar] = {
for {
response1 <- WS.url("http://google.com").get
response2 <- WS.url("http://yahoo.com").get
} yield {
// some code
bar
}
}
Upvotes: 1
Reputation: 44992
(I've never used Akka from Java, so do not fully understand the details of your code, but I hope that I understand your problem anyway)
Short answer: you cannot obtain Bar. And you should never call .get, because it either kills all the advantages of using Futures/Promises (in the best case), or, as already mentioned, leads to deadlocks and kills everything (in the worst case). Once you have stuff inside of a Future[-], it always stays in the Future[-], there is no way back. If you have legacy code that does this:
bar = Func.apply(foo);
doSomethingWithBar(bar)
and you suddenly find out that your Func is no longer synchronous (does not return immediately), but instead asynchronous (makes bunch of long requests), you have to accept that your new asynchronous version of Func cannot live together with the ';'
So you have to reprogram the ';'
Of course, you do not do it yourself, because you already have the Future[-] monad, so you just have to replace the old ';' by the new ';' i.e. map / flatMap, depending on whether your doSomethingWithBar is synchronous or not:
Func.apply(foo).map{ bar =>
doSomethingWithBar(bar)
}
In general, if you previously had something like that:
y = asynch_func1(x);
z = asynch_func2(x, y);
w = synch_func3(x, y, z);
v = asynch_func4(x, y, z, w);
print(v)
and suddenly find out that your functions asynch_func1, asynch_func2, asynch_func4 are now asynchronous and take long time to complete (while synch_func3 remains synchronous, just as example), you rewrite it to:
asynch_func1(x).flatMap{ y =>
asynch_func2(x, y).map{ z =>
synch_func3(x,y,z).flatMap{ w =>
v = asynch_func4(x, y, z, w)
print(v)
}
}
}
I guess it's gonna look pretty horrible (without for-comprehensions and in Java-syntax...), but as long as you have the conceptual clarity and understanding that you are just replacing semicolons by map/flatMap, you should be able to adjust your legacy code pretty quickly and without (too) much headache.
I hope I've answered the right question.
Upvotes: 1