Add error handling to readme
Fix readme
Fix copyright
Simple, functional and value-oriented concurrency primitives for Clojure.
[com.github.ane/task "0.3.0"]
deref
/@
is all you need.future
/promise
can be converted into a task, and vice versa.The task API is built on basic building blocks, run
, then
, compose
and for
.
run
- Compute some value asynchronouslyUse the standard deref
/@
to block the current thread and await the result.
(def my-var (task/run 123))
@(my-var) ; => 123
then
- Apply a function to some asynchronous valuethen
applies a function to the result of another task.
(def async-var (task/run (Thread/sleep 1000)
"asdf"))
@(task/then str/upper-case async-var)
; => "ASDF"
compose
- Compose two asynchronous computationscompose
applies a function producing a task on the result of a a task, and chains their execution together.
(defn comp1 [x]
(task/run (Thread/sleep 1000)
(inc x)))
(defn comp2 [x]
(task/run (Thread/sleep 1000)
(* 2 x)))
@(task/compose comp2 (comp1 4))
; => 10
for
- Compose tasks without the boilerplatefor
lets you apply compose
and then
without the boilerplate. It behaves like let
, it binds
the symbols in the bindings to futures. Once all futures are complete, it evaluates the body.
@(task/for [x (future 1)
y (task/run 2)
c (task/run (Thread/sleep 1000)
7)]
(+ x y c))
; => 10
In the example below, the promise returned by http/get
is automatically converted into a task. The
function then extracts the body, parses it into JSON, gets the title and uppercases it. This
executes asynchronously in another thread, so we have to deref it to print its results The function
then extracts the body, parses it into JSON, gets the title and uppercases it. This executes
asynchronously in another thread, so we have to deref it to print its results.
(def request
(task/then
(fn [data] (-> data
:body
(cheshire/parse-string true)
:title
str/upper-case))
(http/get "http://jsonplaceholder.typicode.com/posts/3")))
(println @request)
compose
to get the result from a POST operationHere we chain two HTTP requests together. First we POST to api-url
with some example data, then
we extract the Location
header, and execute a GET request to that URL. By using compose we don't
end up with nested tasks.
(defn post-and-fetch
[title content]
(task/compose
(fn [response]
(http/get (-> response :headers :location)))
(http/post api-url
{:body (cheshire/generate-string {:userId 123 :title title :body content})})))
@(post-and-fetch "Sample title" "Sample content!")
;; => {:opts {...}, :body "this would be the result", :headers {...}, :status 200}
See Error handling in the user guide
task provides advanced facilities for error handling. Currently the principal method is recover
which accepts a task and a function. The function is passed any exception thrown from the task and
then the value returned by the function. recover
produces a new task:
(def boom (task/run (/ 1 0)))
(def incremented (task/then inc boom))
@(task/recover incremented
(fn [ex]
(println "caught exception: " (.getMessage ex))
123))
;; => 123
Copyright © Antoine Kalmbach. All rights reserved.
Distributed under the Apache License either version 2.0 or (at your option) any later version.