async({...}), with an expression written in its argument, allows that expression to be evaluated in an asynchronous, or non-blocking manner. async returns an object with class c("async", "promise") which implements the promise interface.

async(
  expr,
  ...,
  split_pipes = TRUE,
  compileLevel = getOption("async.compileLevel"),
  debugR = FALSE,
  debugInternal = FALSE,
  trace = getOption("async.verbose")
)

await(prom, error)

Arguments

expr

An expression, to be executed asynchronously.

...

Undocumented.

split_pipes

Rewrite chained calls that use await (see below)

compileLevel

Compilation level; same options as for gen.

debugR

Set TRUE to enter the browser immediately on executing the first R expression.

debugInternal

Set TRUE to single-step at implementation level, immediately upon execution.

trace

Enable verbose logging by passing a function to trace, like trace=cat. This function should take a character argument.

prom

A promise, or something that can be converted to such by promises::as.promise().

error

This argument will be forced if the promise rejects. If it is a function, it will be called with the error condition.

Value

async() returns an object with class "promise," as defined by the promises package (i.e., rather than the kind of promise used in R's lazy evaluation.)

In the context of an async or stream, await(x) returns the resolved value of a promise x, or stops with an error.

Details

An example Shiny app using async/await is on Github: https://github.com/crowding/cranwhales-await

When an async object is activated, it will evaluate its expression until it reaches the keyword await. The async object will return to its caller and preserve the partial state of its evaluation. When the awaited promise is resolved, evaluation continues from where the async left off.

When an async block finishes (either by reaching the end, or using return()), the promise resolves with the resulting value. If the async block stops with an error, the promise is rejected with that error.

Async blocks and generators are conceptually related and share much of the same underlying mechanism. You can think of one as "output" and the other as "input". A generator pauses until a value is requested, runs until it has a value to output, then pauses again. An async runs until it requires an external value, pauses until it receives the value, then continues.

The syntax rules for an async are analogous to those for gen(); await must appear only within the arguments of functions for which there is a pausable implementation (See [pausables()]). For async the default split_pipes=TRUE is enabled; this will rearrange some expressions to satisfy this requirement.

When split_pipes=FALSE, await() can only appear in the arguments of pausables and not ordinary R functions. This is an inconvenience as it prevents using await() in a pipeline. With split_pipes=TRUE applies some syntactic sugar: if an await() appears in the leftmost, unnamed, argument of an R function, the pipe will be "split" at that call using a temporary variable. For instance, either

async(makeRequest() |> await() |> sort())

or, equivalently,

async(sort(await(makeRequest())))

will be effectively rewritten to something like

async({.tmp <- await(makeRequest()); sort(.tmp)})

This works only so long as await appears in calls that evaluate their leftmost arguments normally. split_pipes can backfire if the outer call has other side effects; for instance suppressWarnings(await(x)) will be rewritten as {.tmp <- await(x); suppressWarnings(x)}, which would defeat the purpose.

If async is given a function expression, like async(function(...) ...), it will return an "async function" i.e. a function that constructs an async.

Examples

myAsync <- async(for (i in 1:4) {
  await(delay(5))
  cat(i, "\n")
})