Drop-in replacements for core.async that propagate errors.
In plain core.async an exception thrown inside a go block is
swallowed: the block's channel just closes and the caller sees
nil. Here the exception travels as a value on the channel and is
thrown again by <! in whichever go block takes it. Inside a go
that throw is caught once more and becomes that block's result — so
an error keeps climbing the go block stack until someone catches it,
the way it would in synchronous code.
Stack traces are stitched across the boundary, so a trace shows both
the block that failed and the one that asked for the value. See
athrow.
Everything in this namespace propagates errors that way, and an
exception travels as itself: the class, message and ex-data that
were thrown are the ones caught. Only a thrown value that is no
exception at all — which ClojureScript allows — is lifted into an
ExceptionInfo carrying {:code :unknown, :error x}.
WATCHOUT: Do not mix these with clojure.core.async. Propagation
only works because the error is an ordinary value on the channel — a
plain core.async/<! in between takes that value silently, and the
error is gone with nothing left to notice it by.
Drop-in replacements for `core.async` that propagate errors.
In plain `core.async` an exception thrown inside a `go` block is
swallowed: the block's channel just closes and the caller sees
`nil`. Here the exception travels as a *value* on the channel and is
thrown again by `<!` in whichever go block takes it. Inside a `go`
that throw is caught once more and becomes that block's result — so
an error keeps climbing the go block stack until someone catches it,
the way it would in synchronous code.
Stack traces are stitched across the boundary, so a trace shows both
the block that failed and the one that asked for the value. See
`athrow`.
Everything in this namespace propagates errors that way, and an
exception travels as itself: the class, message and `ex-data` that
were thrown are the ones caught. Only a thrown value that is no
exception at all — which ClojureScript allows — is lifted into an
`ExceptionInfo` carrying `{:code :unknown, :error x}`.
WATCHOUT: Do not mix these with `clojure.core.async`. Propagation
only works because the error is an ordinary value on the channel — a
plain `core.async/<!` in between takes that value silently, and the
error is gone with nothing left to notice it by.(->exception message code x)Turns x into something that can travel a channel as an error.
Anything throwable is handed back untouched — the caller built it
and means it. Anything else is lifted into an ExceptionInfo with
code and the value under :error.
WATCHOUT: That second case is why this exists, and it is not a JVM
concern. JavaScript lets you throw 42, and a promise may reject
with anything at all. Such a value must be lifted, or it would
arrive on the channel indistinguishable from a result and the error
would vanish without a sound.
NOTE: The lifted value goes under :error in the ex-data, not
into the cause. clojure.core/ex-info demands a Throwable
there and throws a ClassCastException on anything else, while
ClojureScript takes whatever it is given — the same code blew up on
one platform and quietly worked on the other.
Turns `x` into something that can travel a channel as an error. Anything throwable is handed back untouched — the caller built it and means it. Anything else is lifted into an `ExceptionInfo` with `code` and the value under `:error`. WATCHOUT: That second case is why this exists, and it is not a JVM concern. JavaScript lets you `throw 42`, and a promise may reject with anything at all. Such a value must be lifted, or it would arrive on the channel indistinguishable from a result and the error would vanish without a sound. NOTE: The lifted value goes under `:error` in the `ex-data`, not into the `cause`. `clojure.core/ex-info` demands a `Throwable` there and throws a `ClassCastException` on anything else, while ClojureScript takes whatever it is given — the same code blew up on one platform and quietly worked on the other.
(<! ?exp)Like core.async/<!, but throws the taken value if it is a
carried error.
This is what propagates an error up the go block stack: inside a
go the throw is caught again and becomes that block's result,
so the error keeps climbing until someone catches it.
WATCHOUT: Catch what was actually thrown. Since exceptions travel
as themselves, a catch ExceptionInfo no longer sees a foreign
exception — that needs Exception (JVM) or :default (cljs).
Like `core.async/<!`, but throws the taken value if it is a carried error. This is what propagates an error up the go block stack: inside a `go` the throw is caught again and becomes that block's result, so the error keeps climbing until someone catches it. WATCHOUT: Catch what was actually thrown. Since exceptions travel as themselves, a `catch ExceptionInfo` no longer sees a foreign exception — that needs `Exception` (JVM) or `:default` (cljs).
(<!! ?exp)Like core.async/<!!, but throws the taken value if it is a
carried error. Blocks the calling thread.
Clojure only — ClojureScript has no blocking take.
Like `core.async/<!!`, but throws the taken value if it is a carried error. Blocks the calling thread. Clojure only — ClojureScript has no blocking take.
(<? sync-or-async-exp)Deprecated, use <?! — it is the same thing under a name that
says how it relates to <! and <!!.
Kept because it is public API and removing it would break callers.
Deprecated, use `<?!` — it is the same thing under a name that says how it relates to `<!` and `<!!`. Kept because it is public API and removing it would break callers.
(<?! sync-or-async-exp)Like <!, but takes a value that may or may not be a channel: a
channel is taken from, anything else is passed through unchanged.
For APIs that return either a ready value or a channel, so the caller does not have to ask which.
Like `<!`, but takes a value that may or may not be a channel: a channel is taken from, anything else is passed through unchanged. For APIs that return either a ready value or a channel, so the caller does not have to ask which.
(<?!! sync-or-async-exp)Like <!!, but takes a value that may or may not be a channel.
Blocks the calling thread.
Clojure only — ClojureScript has no blocking take.
Like `<!!`, but takes a value that may or may not be a channel. Blocks the calling thread. Clojure only — ClojureScript has no blocking take.
(all chs)Waits for all channels chs and yields a vector of their values, in
the order of chs. Alias for (map vector chs).
Propagates the first error among them. With no channels the result
is [].
Waits for all channels `chs` and yields a vector of their values, in the order of `chs`. Alias for `(map vector chs)`. Propagates the first error among them. With no channels the result is `[]`.
(amap <f & xs)Like clojure.core/map, but <f is asynchronous and returns a
channel. Calls may overtake each other and, in Clojure, run in
parallel; ClojureScript is single-threaded and only interleaves
them. The results keep the order of xs either way.
Yields a channel with the vector of results. Propagates errors. A
nil among the results is an ordinary value and keeps its place.
See smap when the calls must not overlap.
Like `clojure.core/map`, but `<f` is asynchronous and returns a channel. Calls may overtake each other and, in Clojure, run in parallel; ClojureScript is single-threaded and only interleaves them. The *results* keep the order of `xs` either way. Yields a channel with the vector of results. Propagates errors. A `nil` among the results is an ordinary value and keeps its place. See `smap` when the calls must not overlap.
(apostwalk <f form)Like clojure.walk/postwalk, but <f is asynchronous and returns
a channel. Visits every node of form innermost first, so <f sees
a node only after its children were replaced.
Calls may overtake each other and, in Clojure, run in parallel. Yields a channel with the walked form. Propagates errors.
Like `clojure.walk/postwalk`, but `<f` is asynchronous and returns a channel. Visits every node of `form` innermost first, so `<f` sees a node only after its children were replaced. Calls may overtake each other and, in Clojure, run in parallel. Yields a channel with the walked form. Propagates errors.
(aprewalk <f form)Like clojure.walk/prewalk, but <f is asynchronous and returns a
channel. Visits every node of form outermost first, so whatever
<f puts in place of a node is walked as well.
Calls may overtake each other and, in Clojure, run in parallel. Yields a channel with the walked form. Propagates errors.
Like `clojure.walk/prewalk`, but `<f` is asynchronous and returns a channel. Visits every node of `form` outermost first, so whatever `<f` puts in place of a node is walked as well. Calls may overtake each other and, in Clojure, run in parallel. Yields a channel with the walked form. Propagates errors.
(areduce <f init coll)Like clojure.core/reduce, but <f is asynchronous and returns a
channel. Reduces coll into init, waiting for each step.
Yields a channel with the result. Propagates errors. A nil or
false in coll is an ordinary item and is reduced like any
other.
Like `clojure.core/reduce`, but `<f` is asynchronous and returns a channel. Reduces `coll` into `init`, waiting for each step. Yields a channel with the result. Propagates errors. A `nil` or `false` in `coll` is an ordinary item and is reduced like any other.
(athrow e)Throws e, after extending its stack trace with the current one.
Without this the trace would end where the go block's thread
began, and the caller that asked for the value would be invisible
— the very context one needs to make sense of the error. The two
halves are separated by an ASYNC_BOUNDARY marker: above it the
frames of the block that failed, below it the frames of the block
that took the value.
Used by <!; rarely needed directly.
Throws `e`, after extending its stack trace with the current one. Without this the trace would end where the go block's thread began, and the caller that asked for the value would be invisible — the very context one needs to make sense of the error. The two halves are separated by an `ASYNC_BOUNDARY` marker: above it the frames of the block that failed, below it the frames of the block that took the value. Used by `<!`; rarely needed directly.
(awalk <inner <outer form)Like clojure.walk/walk, but <inner and <outer are
asynchronous and return channels. Calls may overtake each other and,
in Clojure, run in parallel.
Yields a channel with the walked form. Propagates errors.
Usually reached through apostwalk or aprewalk.
Like `clojure.walk/walk`, but `<inner` and `<outer` are asynchronous and return channels. Calls may overtake each other and, in Clojure, run in parallel. Yields a channel with the walked form. Propagates errors. Usually reached through `apostwalk` or `aprewalk`.
(consume! ch f)Calls f for every value on channel ch. Returns nil right away;
the consuming runs on its own — on a future in Clojure, in a go
block in ClojureScript.
Ends on a closed channel and on a thrown exception. A false in
the stream is an ordinary value and is passed to f like any
other.
Calls `f` for every value on channel `ch`. Returns `nil` right away; the consuming runs on its own — on a `future` in Clojure, in a go block in ClojureScript. Ends on a closed channel and on a thrown exception. A `false` in the stream is an ordinary value and is passed to `f` like any other.
(exception? x)Is x a carried error, i.e. anything the platform can throw?
On the JVM a Throwable, in ClojureScript a js/Error. Everything
throwable travels as itself, so this is the same question as "may
<! throw this again?".
WATCHOUT: This is also the reason an exception cannot be a payload. A value that happens to be an exception object is read as an error here, not as a result. Wrap it if you mean it as data.
Is `x` a carried error, i.e. anything the platform can throw? On the JVM a `Throwable`, in ClojureScript a `js/Error`. Everything throwable travels as itself, so this is the same question as "may `<!` throw this again?". WATCHOUT: This is also the reason an exception cannot be a payload. A value that happens to be an exception object is read as an error here, not as a result. Wrap it if you mean it as data.
(go & body)Like core.async/go, but an exception thrown in body becomes
the block's result instead of being swallowed.
The exception travels as itself — same class, same message, same
ex-data. Only a thrown value that is not an exception at all is
lifted into one, which ClojureScript allows. Take the result with
<! to have it thrown again.
WATCHOUT: On the JVM an Error is deliberately not caught. A
StackOverflowError or OutOfMemoryError says the machine is in
trouble, not that this computation failed; handing it on as an
ordinary channel value would let the program carry on as if it
could. It escapes into core.async's thread instead, which closes
the channel. ClojureScript has no such distinction — everything
there descends from js/Error.
Like `core.async/go`, but an exception thrown in `body` becomes the block's result instead of being swallowed. The exception travels as itself — same class, same message, same `ex-data`. Only a thrown value that is not an exception at all is lifted into one, which ClojureScript allows. Take the result with `<!` to have it thrown again. WATCHOUT: On the JVM an `Error` is deliberately *not* caught. A `StackOverflowError` or `OutOfMemoryError` says the machine is in trouble, not that this computation failed; handing it on as an ordinary channel value would let the program carry on as if it could. It escapes into core.async's thread instead, which closes the channel. ClojureScript has no such distinction — everything there descends from `js/Error`.
(go-loop bindings & body)Like core.async/go-loop, with the error handling of go.
Like `core.async/go-loop`, with the error handling of `go`.
(into coll ch)Like core.async/into, but propagates errors: an error carried on
ch becomes the result instead of ending up in the collection.
Like `core.async/into`, but propagates errors: an error carried on `ch` becomes the result instead of ending up in the collection.
(map f chs)Like core.async/map, but propagates errors: if any channel in
chs carries an error, or f throws, that error becomes the result
instead of being lost.
With no channels at all the result is (f), the same answer
clojure.core gives for folding over nothing — (map + []) yields
0, (map vector []) yields [].
Like `core.async/map`, but propagates errors: if any channel in `chs` carries an error, or `f` throws, that error becomes the result instead of being lost. With no channels at all the result is `(f)`, the same answer `clojure.core` gives for folding over nothing — `(map + [])` yields `0`, `(map vector [])` yields `[]`.
(reduce f init ch)Like core.async/reduce, but propagates errors: an error carried on
ch, or thrown by f, ends the reduction and becomes the result.
Like `core.async/reduce`, but propagates errors: an error carried on `ch`, or thrown by `f`, ends the reduction and becomes the result.
(smap <f & xs)Like clojure.core/map, but <f is asynchronous and returns a
channel. Calls happen strictly one after another in the order of
xs, each waiting for the one before it.
Yields a channel with the vector of results. Propagates errors.
Stops when the shortest collection runs out, like
clojure.core/map; a nil or false among the elements is an
ordinary value.
See amap for the variant that may run in parallel.
Like `clojure.core/map`, but `<f` is asynchronous and returns a channel. Calls happen strictly one after another in the order of `xs`, each waiting for the one before it. Yields a channel with the vector of results. Propagates errors. Stops when the shortest collection runs out, like `clojure.core/map`; a `nil` or `false` among the elements is an ordinary value. See `amap` for the variant that may run in parallel.
(thread & body)Like core.async/thread, with the error handling of go. Runs
body on a real thread, so it may block.
Clojure only.
Like `core.async/thread`, with the error handling of `go`. Runs `body` on a real thread, so it may block. Clojure only.
(thread-call f)(thread-call f workload)Like core.async/thread-call, with the error handling of go:
an exception from f becomes the channel's value instead of
escaping into the thread pool unnoticed.
workload says what f does, so core.async can route it to the
right pool — :io, :compute or :mixed (the default).
WATCHOUT: The workload argument needs core.async 1.8.730 or
newer. It is the consumer's version that counts, not the one
pinned here — a :dependencies entry in a library is routinely
overridden downstream, and an older core.async answers every
single call with an ArityException.
Clojure only.
Like `core.async/thread-call`, with the error handling of `go`: an exception from `f` becomes the channel's value instead of escaping into the thread pool unnoticed. `workload` says what `f` does, so core.async can route it to the right pool — `:io`, `:compute` or `:mixed` (the default). WATCHOUT: The workload argument needs core.async 1.8.730 or newer. It is the consumer's version that counts, not the one pinned here — a `:dependencies` entry in a library is routinely overridden downstream, and an older core.async answers every single call with an `ArityException`. Clojure only.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |