3 ms·
With Loom, what's the equivalent of the following Promise concepts? 1. Promise constructor (to turn an existing callback-based or async operation into one that
by binarynate 4y ago
With Loom, what's the equivalent of the following Promise concepts?
1. Promise constructor (to turn an existing callback-based or async operation into one that can be blocked with Loom). JS example:
await new Promise(resolve => someApiThatTakesACallback(resolve));
2. Methods like Promise.all() and Promise.any() to run multiple async operations concurrently. JS example:
const [result1, result2] = await Promise.all([doSomething1(), doSomething2()]);
- christophilus 4y agoMy guess for the latter is something like goroutines and channels, though I’m not sure. So: spin up a thread per “doSomething” and then join those in your main thread.
- teh64 4y agoFor the first one, I believe this is the correct method [2], which takes a Callable (the other submit methods take a Runnable, which does not seem to be generic). For the second one, you can use an implementation of ExecutorService[1], which has the methods invokeAll and invokeAny. Futures in Java are similar to Promises in Javascript, in that they represent a calculation that might not be completed yet. 1. https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ExecutorService.html https://docs.oracle.com/en/java/javase/17/docs/api/java.base... 2. https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ExecutorService.html#submit(java.util.concurrent.Callable) https://docs.oracle.com/en/java/javase/17/docs/api/java.base...
- ackfoobar 4y agoThe parent comment meant `callbackTakingFunction: ((Res, Throwable) -> ()) -> ()`, not `blockingFunction: () -> Res` `ExecutorService#submit` works with the latter.
- binarynate 4y agoThank you!
- ackfoobar 4y ago1. Callback interop I was expecting something like call/cc, or `suspend(Cancellable)Coroutine` in Kotlin. But judging from the list of preview items[1] it seems the only way is to have the callback write the result to a Future and blocking wait on it. 2.1 Promise.all() can be done with `.map(future => future.get())` But it is a bit more complicated than that. [2] 2.2 Promise.any() This is somewhat relevant http://mail.openjdk.java.net/pipermail/loom-dev/2020-February/001019.html http://mail.openjdk.java.net/pipermail/loom-dev/2020-Februar... [1] https://download.java.net/java/early_access/loom/docs/api/preview-list.html https://download.java.net/java/early_access/loom/docs/api/pr... [2] https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/await-all.html https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutin... "This function is not equivalent to deferreds.map { it.await() } which fails only when it sequentially gets to wait for the failing deferred, while this awaitAll fails immediately as soon as any of the deferreds fail."
- binarynate 4y agoThanks!
- twic 4y agoHere are my guesses. 1. Same as with normal threads: CompletableFuture<Foo> future = new CompletableFuture<>(); someApiThatTakesACallback(future::complete); Foo result = future.get(); // this can take a timeout 2. If you mean that the operations are blocking and should be called in parallel using virtual threads, the equivalent of Promise.any:: ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); Future<Foo> future1 = executor.submit(this::doSomething1); Future<Bar> future2 = executor.submit(this::doSomething2); Foo result1 = future1.get(); Bar result2 = future2.get(); There is a version of that using ExecutorService::invokeAll, but i'm not sure it's any more concise, because you still need to unpack the futures one by one. For Promise.any: ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); Foo result = executor.invokeAny(List.of(this::doSomething1, this::doSomething2)); The asymmetry between invokeAny and invokeAll here is slightly grating, but makes sense. Note that in both cases, you probably already have an executor lying around that you can use for this.
- binarynate 4y agoThanks! Your examples are super helpful.
- samus 4y agoDo note that the `Executor` interface now extends `AutoCloseable` and can now be used with the `try-with-resources`-statement. This is the most important building block for the new Structured Concurrency approach!
- papercrane 4y agoIn addition to the other comments, the other way to achieve this with loom is to use the new structured concurrency API. This is overkill for a single task, but for your second example you could do a an invokeAny with something like this: try (var scope = new StructuredTaskScope.ShutdownOnSuccess<String>()) { scope.fork(() -> doSomething1()); scope.fork(() -> doSomething2()); scope.join(); // or if you wanted a 5 second timeout .joinUntil(Instant.now().plusSeconds(5)) String result = scope.result(); // This will return the result of either doSomething1() or doSomething2(), whichever won the race to finish first. ... } https://download.java.net/java/early_access/loom/docs/api/jdk.incubator.concurrent/jdk/incubator/concurrent/StructuredTaskScope.html https://download.java.net/java/early_access/loom/docs/api/jd...