Turns callback-based functions into channels, with the error
propagation of jtk-dvlp.async.
A callback API does not fit into a sequence of steps: the rest of
the work has to move inside the callback, and every further call
nests one level deeper. cb->c turns such a call into a channel, so
it reads like any other step in a go block — and a failure arrives
as a thrown error rather than as a second callback.
WATCHOUT: cb->c is a macro that rewrites the expression handed
to it. It looks for the symbols callback, resolve and reject
inside and puts its own functions in their place. Those symbols are
therefore written bare, without ever being defined — and they are
only found where they literally stand. Move a callback into a helper
function and the macro cannot see it any more.
Turns callback-based functions into channels, with the error propagation of `jtk-dvlp.async`. A callback API does not fit into a sequence of steps: the rest of the work has to move inside the callback, and every further call nests one level deeper. `cb->c` turns such a call into a channel, so it reads like any other step in a go block — and a failure arrives as a thrown error rather than as a second callback. WATCHOUT: `cb->c` is a macro that *rewrites* the expression handed to it. It looks for the symbols `callback`, `resolve` and `reject` inside and puts its own functions in their place. Those symbols are therefore written bare, without ever being defined — and they are only found where they literally stand. Move a callback into a helper function and the macro cannot see it any more.
(<cb! ?exp)Like jtk-dvlp.async/<!, but for a callback-based call: takes
the value the callback was given, or throws if it was rejected.
Shorthand for (<! (cb->c ?exp)).
Takes the same marks as cb->c — see there.
Like `jtk-dvlp.async/<!`, but for a callback-based call: takes the value the callback was given, or throws if it was rejected. Shorthand for `(<! (cb->c ?exp))`. Takes the same marks as `cb->c` — see there.
(cb->c exp)(cb->c [f & forms :as _exp] auto-close?)Creates a channel from the callbacks of exp.
The symbols callback, resolve and reject mark the callback
positions in exp; whatever they are called with is put onto the
new channel — reject as a carried error. Without any mark, the
callback is assumed to be the last argument of exp and is
appended.
The callbacks of exp must take exactly one argument.
A rejection that is an exception travels as itself; anything
else is lifted into an ExceptionInfo with
{:code :callback-error} and the value under :error. If
calling exp itself throws, that error goes onto the channel
too, and the channel is closed.
With auto-close? (the default) the channel is closed after the
first put, whether resolution or rejection — right for a call
that answers once. Pass false for a source that calls back
repeatedly; then closing is up to the caller.
WATCHOUT: The marks are found by walking exp for those literal
symbols. A callback that lives in a helper function instead of
standing inline is invisible to the macro — which is why the
reject below sits in an inline fn.
Example:
(require '[jtk-dvlp.async :as a])
(defn read-file
[path on-success on-failure]
,,,)
(a/go
(try
(println
(<cb!
(read-file
"/etc/hosts"
resolve
;; Inline, so that `cb->c` can see the `reject` mark.
(fn add-context-before-rejecting [error]
(->> {:code :read-failed, :path "/etc/hosts"}
(ex-info "could not read file")
(reject))))))
(catch ExceptionInfo e
(println "failed:" (ex-message e) (ex-data e)))))
Creates a channel from the callbacks of `exp`.
The symbols `callback`, `resolve` and `reject` mark the callback
positions in `exp`; whatever they are called with is put onto the
new channel — `reject` as a carried error. Without any mark, the
callback is assumed to be the last argument of `exp` and is
appended.
The callbacks of `exp` must take exactly one argument.
A rejection that is an exception travels as itself; anything
else is lifted into an `ExceptionInfo` with
`{:code :callback-error}` and the value under `:error`. If
calling `exp` itself throws, that error goes onto the channel
too, and the channel is closed.
With `auto-close?` (the default) the channel is closed after the
first put, whether resolution or rejection — right for a call
that answers once. Pass `false` for a source that calls back
repeatedly; then closing is up to the caller.
WATCHOUT: The marks are found by walking `exp` for those literal
symbols. A callback that lives in a helper function instead of
standing inline is invisible to the macro — which is why the
`reject` below sits in an inline `fn`.
Example:
```clojure
(require '[jtk-dvlp.async :as a])
(defn read-file
[path on-success on-failure]
,,,)
(a/go
(try
(println
(<cb!
(read-file
"/etc/hosts"
resolve
;; Inline, so that `cb->c` can see the `reject` mark.
(fn add-context-before-rejecting [error]
(->> {:code :read-failed, :path "/etc/hosts"}
(ex-info "could not read file")
(reject))))))
(catch ExceptionInfo e
(println "failed:" (ex-message e) (ex-data e)))))
```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 |