All Projects → metosin → Sieppari

metosin / Sieppari

Licence: epl-2.0
Small, fast, and complete interceptor library for Clojure/Script

Programming Languages

clojure
4091 projects
clojurescript
191 projects

Projects that are alternatives of or similar to Sieppari

Vue Loadable
⏳ Improve your loading state control with pretty simple methods and helpers.
Stars: ✭ 23 (-82.71%)
Mutual labels:  async, promise, interceptor
Flowa
🔥Service level control flow for Node.js
Stars: ✭ 66 (-50.38%)
Mutual labels:  async, promise
Promised Pipe
A ramda.pipe-like utility that handles promises internally with zero dependencies
Stars: ✭ 64 (-51.88%)
Mutual labels:  async, promise
Datakernel
Alternative Java platform, built from the ground up - with its own async I/O core and DI. Ultra high-performance, simple and minimalistic - redefines server-side programming, web-development and highload!
Stars: ✭ 87 (-34.59%)
Mutual labels:  async, promise
Emacs Async Await
Async/Await for Emacs
Stars: ✭ 47 (-64.66%)
Mutual labels:  async, promise
Before After Hook
wrap methods with before/after hooks
Stars: ✭ 49 (-63.16%)
Mutual labels:  async, promise
Write
Write data to the file system, creating any intermediate directories if they don't already exist. Used by flat-cache and many others!
Stars: ✭ 68 (-48.87%)
Mutual labels:  async, promise
Create Request
Apply interceptors to `fetch` and create a custom request function.
Stars: ✭ 34 (-74.44%)
Mutual labels:  async, promise
Tedis
redis client with typescript and esnext for nodejs
Stars: ✭ 109 (-18.05%)
Mutual labels:  async, promise
Jdeferred
Java Deferred/Promise library similar to JQuery.
Stars: ✭ 1,483 (+1015.04%)
Mutual labels:  async, promise
Kitchen Async
A Promise library for ClojureScript, or a poor man's core.async
Stars: ✭ 128 (-3.76%)
Mutual labels:  async, promise
Node Qiniu Sdk
七牛云SDK,使用 ES2017 async functions 来操作七牛云,接口名称与官方接口对应,轻松上手,文档齐全
Stars: ✭ 44 (-66.92%)
Mutual labels:  async, promise
Breeze
Javascript async flow control manager
Stars: ✭ 38 (-71.43%)
Mutual labels:  async, promise
Download
Download and extract files
Stars: ✭ 1,064 (+700%)
Mutual labels:  async, promise
Fritzbox.js
☎️ The leading AVM Fritz!Box API for NodeJS and JavaScript.
Stars: ✭ 36 (-72.93%)
Mutual labels:  async, promise
Emittery
Simple and modern async event emitter
Stars: ✭ 1,146 (+761.65%)
Mutual labels:  async, promise
Rubico
[a]synchronous functional programming
Stars: ✭ 133 (+0%)
Mutual labels:  async, promise
Nodespider
[DEPRECATED] Simple, flexible, delightful web crawler/spider package
Stars: ✭ 33 (-75.19%)
Mutual labels:  async, promise
Taskorama
⚙ A Task/Future data type for JavaScript
Stars: ✭ 90 (-32.33%)
Mutual labels:  async, promise
Bach
Compose your async functions with elegance.
Stars: ✭ 117 (-12.03%)
Mutual labels:  async, promise

sieppari cljdoc badge

Small, fast, and complete interceptor library for Clojure/Script with built-in support for common async libraries.

Noun Siepata (Intercept)

sieppari, someone or something that intercepts

What it does

Interceptors, like in Pedestal, but with minimal implementation and optimal performance.

The core Sieppari depends on Clojure and nothing else.

If you are new to interceptors, check the Pedestal Interceptors documentation. Sieppari's sieppari.core/execute follows a :request / :response pattern. For Pedestal-like behavior, use sieppari.core/execute-context.

First example

(ns example.simple
  (:require [sieppari.core :as s]))

;; interceptor, in enter update value in `[:request :x]` with `inc`
(def inc-x-interceptor
  {:enter (fn [ctx] (update-in ctx [:request :x] inc))})

;; handler, take `:x` from request, apply `inc`, and return an map with `:y`
(defn handler [request]
  {:y (inc (:x request))})

(s/execute
  [inc-x-interceptor handler]
  {:x 40})
;=> {:y 42}

Async

Any step in the execution pipeline (:enter, :leave, :error) can return either a context map (synchronous execution) or an instance of AsyncContext - indicating asynchronous execution.

By default, clojure deferrables, java.util.concurrent.CompletionStage and js/promise satisfy the AsyncContext protocol.

Using s/execute with async steps will block:

;; async interceptor, in enter double value of `[:response :y]`:
(def multiply-y-interceptor
  {:leave (fn [ctx]
            (future
              (Thread/sleep 1000)
              (update-in ctx [:response :y] * 2)))})


(s/execute
  [inc-x-interceptor multiply-y-interceptor handler]
  {:x 40})
; ... 1 second later:
;=> {:y 84}

Using non-blocking version of s/execute:

(s/execute
  [inc-x-interceptor multiply-y-interceptor handler]
  {:x 40}
  (partial println "SUCCESS:")
  (partial println "FAILURE:"))
; => nil
; prints "SUCCESS: {:y 84}" 1sec later

Blocking on async computation:

(let [respond (promise)
      raise (promise)]
  (s/execute
    [inc-x-interceptor multiply-y-interceptor handler]
    {:x 40}
    respond
    raise) ; returns nil immediately

  (deref respond 2000 :timeout))
; ... 1 second later:
;=> {:y 84}

Any step can return a java.util.concurrent.CompletionStage or js/promise, Sieppari works oob with libraries like Promesa:

;; [funcool/promesa "5.1.0"]`
(require '[promesa.core :as p])

(def chain
  [{:enter #(update-in % [:request :x] inc)}               ;; 1
   {:leave #(p/promise (update-in % [:response :x] / 10))} ;; 4
   {:enter #(p/delay 1000 %)}                              ;; 2
   identity])                                              ;; 3

;; blocking
(s/execute chain {:x 40})
; => {:x 41/10} after after 1sec

;; non-blocking
(s/execute
  chain
  {:x 40}
  (partial println "SUCCESS:")
  (partial println "SUCCESS:"))
; => nil
;; prints "SUCCESS: {:x 41/10}" after 1sec

External Async Libraries

To add a support for one of the supported external async libraries, just add a dependency to them and require the respective Sieppari namespace. Currently supported async libraries are:

  • core.async - sieppari.async.core-async, clj & cljs
  • Manifold - sieppari.async.manifold clj

To extend Sieppari async support to other libraries, just extend the AsyncContext protocol.

core.async

Requires dependency to [org.clojure/core.async "0.4.474"] or higher.

(require '[clojure.core.async :as a])

(defn multiply-x-interceptor [n]
  {:enter (fn [ctx]
            (a/go (update-in ctx [:request :x] * n)))})

(s/execute
  [inc-x-interceptor (multiply-x-interceptor 10) handler]
  {:x 40})
;=> {:y 411}

manifold

Requires dependency to [manifold "0.1.8"] or higher.

(require '[manifold.deferred :as d])

(defn minus-x-interceptor [n]
  {:enter (fn [ctx]
            (d/success-deferred (update-in ctx [:request :x] - n)))})

(s/execute
  [inc-x-interceptor (minus-x-interceptor 10) handler]
  {:x 40})
;=> {:y 31}

Performance

Sieppari aims for minimal functionality and can therefore be quite fast. Complete example to test performance is included.

Silly numbers

Executing a chain of 10 interceptors, which have :enter of clojure.core/identity.

  • sync: all steps return the ctx
  • promesa: all steps return the ctx in an promesa.core/promise
  • core.async: all step return the ctx in a core.async channel
  • manifold: all step return the ctx in a manifold.deferred.Deferred

All numbers are execution time lower quantile (not testing the goodness of the async libraries , just the execution overhead sippari interceptors adds)

Executor sync promesa core.async manifold
Pedestal 8.2µs - 92µs -
Sieppari 1.2µs 4.0µs 70µs 110µs
Middleware (comp) 0.1µs - - -
  • MacBook Pro (Retina, 15-inch, Mid 2015), 2.5 GHz Intel Core i7, 16 MB RAM
  • Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
  • Clojure 1.9.0

NOTE: running async flows without interceptors is still much faster, e.g. synchronous manifold chain is much faster than via interceptors.

NOTE: Goal is to have a Java-backed and optimized chain compiler into Sieppari, initial tests show it will be near the perf of middleware chain / comp.

Differences to Pedestal

Execution

  • io.pedestal.interceptor.chain/execute executes Contexts
  • sieppari.core/execute executes Requests (which are internally wrapped inside a Context for interceptors)

Errors

  • In Pedestal the error handler takes two arguments, the ctx and the exception.
  • In Sieppari the error handlers takes just one argument, the ctx, and the exception is in the ctx under the key :error.
  • In Pedestal the error handler resolves the exception by returning the ctx, and continues the error stage by re-throwing the exception.
  • In Sieppari the error handler resolves the exception by returning the ctx with the :error removed. To continue in the error stage, just return the ctx with the exception still at :error.
  • In Pedestal the exception are wrapped in other exceptions.
  • In Sieppari exceptions are not wrapped.
  • Pedestal interception execution catches java.lang.Throwable for error processing. Sieppari catches java.lang.Exception. This means that things like out of memory or class loader failures are not captured by Sieppari.

Async

  • Pedestal transfers thread local bindings from call-site into async interceptors.
  • Sieppari does not support this.

Thanks

License

Copyright © 2018-2020 Metosin Oy

Distributed under the Eclipse Public License 2.0.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].