-
Notifications
You must be signed in to change notification settings - Fork 17
Description
chromote offers a $wait_for(promise) method that would be useful in many other contexts. In particular, it'd be a useful way to turn an synchronously call an asynchronous tool in ellmer.
I propose we bring the code from chromote/R/synchronize.R into promises as a function called wait_for().
p <- promises::promise({ ...async code... })
p$then(function(value) { ... more async ... })
value <- promises::wait_for(p) # throws if promise is rejectedI believe it will also need an argument to choose which event loop is used, although it would be helpful if @wch or someone with experience there could describe the requirements for that feature.
Research (from o3)
Below is a non-exhaustive “field guide” to the most common names used in mainstream languages for “take an asynchronous/promise-like object and block the current thread until it is fulfilled (or rejected/errored).”
| Language | Object Type | Blocking Call / Construct | Remarks |
|---|---|---|---|
| Java | CompletableFuture<T> |
get() or join() |
get() throws checked exceptions; join() wraps them in CompletionException. |
| Kotlin | Deferred<T> / Job |
runBlocking { … } wrapper or Deferred.await() inside a runBlocking |
await() is suspending; only runBlocking makes the current thread wait. |
| Scala | Future[T] |
Await.result(future, duration) |
Requires an explicit timeout (Duration.Inf for “forever”). |
| C# / .NET | Task<T> |
Task.Wait() or Task.Result |
await does not block—use only inside async methods. |
| C++11+ | std::future<T> |
get() or wait() |
get() returns the value (and re-throws); wait() just blocks until ready. |
| Rust | Future |
futures::executor::block_on(fut) or tokio::runtime::Runtime::block_on(fut) |
No language-level await that blocks; executors provide it. |
| Python | asyncio.Future / coroutine |
asyncio.get_event_loop().run_until_complete(coro) |
Inside coroutines use await, but that’s non-blocking to the loop. |
| JavaScript / TypeScript (Node) | Promise<T> |
await within an async function is non-blocking; to truly block the thread you’d need third-party helpers (rare). |
|
| Go | chan T (also *sync.WaitGroup) |
<-chan receive or wg.Wait() |
Receiving from a channel / WaitGroup blocks the goroutine until ready. |
| Swift | Task<T> (Structured Concurrency) |
Task.runAndBlock { … } (Swift 5.10) or Dispatch semaphores |
Direct blocking is discouraged; bridging APIs exist. |
| Haskell | IO (Async a) |
wait async |
In Control.Concurrent.Async. |
Observations you can reuse for R naming
- The word “wait” or “get” dominates when the call returns the promised value synchronously.
- “join” is also common (Java, C, POSIX threads) but usually connotes waiting for completion, not necessarily retrieving a value.
- “block_*” or “runBlocking” explicitly advertises that blocking is happening (Kotlin, Rust).
- Languages with
awaitreserve that keyword for non-blocking suspension inside async code; they supply a separate blocking helper.
Therefore, if your R helper literally suspends the calling R thread until the promise resolves, idiomatic candidates would be:
wait(promise)– shortest, mirrors Java/Scala/Haskellget(promise)– emphasizes the returned value like C++/Javajoin(promise)– if you just care about completion statusblock_until(promise)orblock(promise)– makes the blocking explicitrun_blocking(expr)– if you wrap an expression rather than a promise object
Pick whichever aligns best with the existing naming style in your package or the tidyverse conventions (verbs, snake_case).