std/async/async▲toc

Asynchronous primitives.

This module is based closely on [1] and aims to have robust and composable asynchronous primitives. In particular, any outstanding asynchronous operation can be canceled (through cancel-outstandingstd/async/async/cancel-outstanding: () -> async-cancel ()) within a certain cancelation scope which allows for composable primitives like timeout and interleave.

1. Cancelation

When calling cancel-outstandingstd/async/async/cancel-outstanding: () -> async-cancel (), it cancels any outstanding async operations in the current cancelation scope and any of its child scopes. Some reasons for cancelation: - returning normally, cancelation prevents dangling callbacks - returning abnormally (e.g. an exception), we cancel any pending callbacks - within a firstof, we have decided to cancel all remaining strands after the first is one completes

After cancel-outstandingstd/async/async/cancel-outstanding: () -> async-cancel () is called, the caller continues as normal - calling cancel-oustanding simply returns ().

The way that cancelation is observed is that all outstanding async operaions in the cancelation scope receive a Cancelstd/async/internal/Cancel: forall<a> await-result<a> result instead of a Resultstd/async/internal/Result: forall<a> (value : a) -> await-result<a> (or Exception). Aside from low-level code, a user won't observe this as the abstractions awaiting a result automatically use discontinuestd/async/async/discontinue: forall<a> () -> discontinue a() when Cancelstd/async/internal/Cancel: forall<a> await-result<a> a result is given.

We use a separate effect discontinuestd/async/async/discontinue: (E, V) -> V instead of an exception since it is intended that a discontinuestd/async/async/discontinue: forall<a> () -> discontinue a is never handled and always discontinues a stsrand. Of course, finallystd/core/hnd/finally: forall<a,e> (fin : () -> e (), action : () -> e a) -> e a blocks still execute so external resources can be released properly.

2. References

[1]Daan Leijen. Structured Asynchrony with Algebraic Effects. Microsoft Research technical report MSR-TR-2017-21, May 2017. pdf 🔎

.

alias asyncstd/async/async/async: E = <async-awaitstd/async/internal/async-await: (E, V) -> V,async-iocstd/async/internal/async-ioc: (E, V) -> V,async-cancelstd/async/internal/async-cancel: (E, V) -> V,discontinuestd/async/async/discontinue: (E, V) -> V>

The asynchronous effect is composed of four primitive effects. async-awaitstd/async/internal/async-await: (E, V) -> V allows awaiting asynchronous operations, async-cancelstd/async/internal/async-cancel: (E, V) -> V allows cancelation of outstanding asynchronous operations, async-iocstd/async/internal/async-ioc: (E, V) -> V allows execution of linear I/O (iocstd/core/ioc: E) at the handler scope, and discontinuestd/async/async/discontinue: (E, V) -> V discontinues any running strand (using final ctl).

The main asyncstd/async/async/async: E handler. Uses “libuv” for the C backend, (with a libuv emscripten wrapper for the Wasm Web backend). Uses the standard async calls for Javascript (web or Node).

type discontinuestd/async/async/discontinue: (E, V) -> V<e,a>
fun await-result/show( r : await-resultstd/async/internal/await-result: V -> V<a>, ?show : (a) -> stringstd/core/types/string: V ) : stringstd/core/types/string: V

Show an await result.

fun duration/wait( secs : ? durationstd/time/duration/duration: V ) : asyncstd/async/async/async: E ()

Wait (asynchronously) for optional secs seconds durationstd/time/duration/duration: V (= 0.seconds or 100.milli-seconds). Use yieldstd/async/async/yield: () -> async ()() to yield generally to other asynchronous operations.

fun float/wait( secs : float64std/core/types/float64: V ) : asyncstd/async/async/async: E ()

Wait (asynchronously) for secs seconds as a double. Use yieldstd/async/async/yield: () -> async ()() to yield to other asynchronous operations.

fun setup/await( label : ? stringstd/core/types/string: V, setup : (evloop : event-loopstd/async/internal/event-loop: V, resume : (errorstd/core/exn/error: V -> V<a>) -> iocstd/core/ioc: E ()) -> iocstd/core/ioc: E errorstd/core/exn/error: V -> V<dispose-fnstd/async/internal/dispose-fn: V> ) : <asyncstd/async/async/async: E,exnstd/core/exn/exn: (E, V) -> V> a

Await with the exception effect.

fun setup/await-error( label : stringstd/core/types/string: V, setup : (evloop : event-loopstd/async/internal/event-loop: V, resume : (a) -> iocstd/core/ioc: E ()) -> iocstd/core/ioc: E errorstd/core/exn/error: V -> V<dispose-fnstd/async/internal/dispose-fn: V> ) : asyncstd/async/async/async: E errorstd/core/exn/error: V -> V<a>

Primitive await: call setup to set up an asynchronous callback with the host platform. The setup is passed the eventloop of the async handler, and a resumestd/core/hnd/resume: forall<a,e,e1,b> (r : resume-context<a,e,e1,b>, x : a) -> e b function that takes an await-resultstd/async/internal/await-result: V -> V: a cancelation, exception, or regular result. The setup function returns either an error, or a dispose funcion that is invoked on cancellation to dispose of any resources (see the implementation of wait). The resumestd/core/hnd/resume: forall<a,e,e1,b> (r : resume-context<a,e,e1,b>, x : a) -> e b is invoked exactly once (either on the async completion callback, or on cancelation).

fun setup/await-error1( label : stringstd/core/types/string: V, setup : (evloop : event-loopstd/async/internal/event-loop: V, resume : (errorstd/core/exn/error: V -> V<a>) -> iocstd/core/ioc: E ()) -> iocstd/core/ioc: E errorstd/core/exn/error: V -> V<dispose-fnstd/async/internal/dispose-fn: V> ) : asyncstd/async/async/async: E errorstd/core/exn/error: V -> V<a>
fun async-st( action : () -> ststd/core/types/st: H -> E<globalstd/core/types/global: H> a ) : asyncstd/async/async/async: E a

Perform a stateful operation in the outer async handler.

fun await0( label : ? stringstd/core/types/string: V, setup : (resume : () -> iocstd/core/ioc: E ()) -> iocstd/core/ioc: E errorstd/core/exn/error: V -> V<dispose-fnstd/async/internal/dispose-fn: V> ) : <asyncstd/async/async/async: E,exnstd/core/exn/exn: (E, V) -> V> ()

Await a zero argument callback.

fun await1( label : ? stringstd/core/types/string: V, setup : (resume : (a) -> iocstd/core/ioc: E ()) -> iocstd/core/ioc: E errorstd/core/exn/error: V -> V<dispose-fnstd/async/internal/dispose-fn: V> ) : <asyncstd/async/async/async: E,exnstd/core/exn/exn: (E, V) -> V> a

Await a single argument callback.

fun await1-noexn( label : ? stringstd/core/types/string: V, setup : (resume : (a) -> iocstd/core/ioc: E ()) -> iocstd/core/ioc: E () ) : asyncstd/async/async/async: E a

Convenience function for awaiting a single argument callback that never fails.

fun cancel-outstanding(): async-cancelstd/async/internal/async-cancel: (E, V) -> V ()

Cancel any other outstanding operations that are interleaved and part of the same cancelation scope (see cancelation-scopestd/async/async/cancelation-scope: forall<a,e> (action : () -> <async|e> a) -> <async|e> a).

fun cancelation-scope( action : () -> <asyncstd/async/async/async: E|e> a ) : <asyncstd/async/async/async: E|e> a

Set up a new cancelation scope.

fun do-sync( action : (evloop : event-loopstd/async/internal/event-loop: V) -> iocstd/core/ioc: E errorstd/core/exn/error: V -> V<()>, label : ? stringstd/core/types/string: V ) : <asyncstd/async/async/async: E,exnstd/core/exn/exn: (E, V) -> V> ()
fun in-parent-scope( action : () -> <asyncstd/async/async/async: E|e> a ) : <asyncstd/async/async/async: E|e> a

Run an action as if it is part of the parent scope (so it will be canceled only if the parent scope is canceled).

fun is-canceled(): async-cancelstd/async/internal/async-cancel: (E, V) -> V boolstd/core/types/bool: V

Is the current cancelation canceled?

fun unsafe-uncancel( action : () -> <asyncstd/async/async/async: E|e> a ) : <asyncstd/async/async/async: E|e> maybestd/core/types/maybe: V -> V<a>

Catch a discontinue as a maybestd/core/types/maybe: V -> V. Use with care as cancelation should not be ignored.

fun yield(): asyncstd/async/async/async: E ()

Yield to other asynchronous operations. Same as wait(0).

private import std/core/typesstd/core/types, std/core/hndstd/core/hnd, std/core/exnstd/core/exn, std/core/boolstd/core/bool, std/core/orderstd/core/order, std/core/charstd/core/char, std/core/intstd/core/int, std/core/vectorstd/core/vector, std/core/bytesstd/core/bytes, std/core/bslicestd/core/bslice, std/core/stringstd/core/string, std/core/sslicestd/core/sslice, std/core/liststd/core/list, std/core/maybestd/core/maybe, std/core/maybe2std/core/maybe2, std/core/eitherstd/core/either, std/core/resultstd/core/result, std/core/tuplestd/core/tuple, std/core/lazystd/core/lazy, std/core/showstd/core/show, std/core/debugstd/core/debug, std/core/delayedstd/core/delayed, std/core/consolestd/core/console, std/corestd/core, std/num/int64std/num/int64, std/core/undivstd/core/undiv, std/core/unsafestd/core/unsafe, std/num/float64std/num/float64, std/time/durationstd/time/duration, std/async/internalstd/async/internal, std/async/api/uv/evloopstd/async/api/uv/evloop, std/num/ddoublestd/num/ddouble, std/time/timestampstd/time/timestamp