All Projects → fertapric → Async_with

fertapric / Async_with

Licence: mit
The asynchronous version of Elixir's "with", resolving the dependency graph and executing the clauses in the most performant way possible!

Programming Languages

elixir
2628 projects

Labels

Projects that are alternatives of or similar to Async with

Async Redis
First class async & promise support for redis.
Stars: ✭ 128 (-7.91%)
Mutual labels:  async
Sieppari
Small, fast, and complete interceptor library for Clojure/Script
Stars: ✭ 133 (-4.32%)
Mutual labels:  async
Cppcoro
A library of C++ coroutine abstractions for the coroutines TS
Stars: ✭ 2,118 (+1423.74%)
Mutual labels:  async
Lealone
极具创新的面向微服务和 OLTP/OLAP 场景的单机与分布式关系数据库
Stars: ✭ 1,802 (+1196.4%)
Mutual labels:  async
Zhttp
基于swoole的异步轻量级web框架,内部封装协程异步非阻塞全套mysql、redis、mongo、memcached连接池,可以轻松start、reload、stop,加入数据库的查询模块,框架已经封装好近乎同步写法,底层异步调用
Stars: ✭ 131 (-5.76%)
Mutual labels:  async
Vkwave
Asynchronous framework for building high-performance & easy to scale projects interacting with VK's API.
Stars: ✭ 135 (-2.88%)
Mutual labels:  async
Redux Act Async
Reduce boilerplate for redux async actions and reducers
Stars: ✭ 127 (-8.63%)
Mutual labels:  async
Angular Async Loader
Load modules and components asynchronously for angular 1.x application.
Stars: ✭ 137 (-1.44%)
Mutual labels:  async
Rubico
[a]synchronous functional programming
Stars: ✭ 133 (-4.32%)
Mutual labels:  async
Nim Chronos
Chronos - An efficient library for asynchronous programming
Stars: ✭ 136 (-2.16%)
Mutual labels:  async
Guwen Spider
一个完整的nodeJs 串行爬虫 抓取3万多个页面。
Stars: ✭ 129 (-7.19%)
Mutual labels:  async
Spotify.py
🌐 API wrapper for Spotify 🎶
Stars: ✭ 131 (-5.76%)
Mutual labels:  async
Actix Web
Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.
Stars: ✭ 12,723 (+9053.24%)
Mutual labels:  async
Kitchen Async
A Promise library for ClojureScript, or a poor man's core.async
Stars: ✭ 128 (-7.91%)
Mutual labels:  async
Futures Intrusive
Synchronization primitives for Futures and async/await based on intrusive collections
Stars: ✭ 137 (-1.44%)
Mutual labels:  async
Tas
Make it easy to develop large, complex Node.js app.
Stars: ✭ 128 (-7.91%)
Mutual labels:  async
Pyrogram
Telegram MTProto API Client Library and Framework in Pure Python for Users and Bots
Stars: ✭ 2,252 (+1520.14%)
Mutual labels:  async
Hydro
Ultra-pure, lag-free prompt with async Git status. Designed for Fish.
Stars: ✭ 137 (-1.44%)
Mutual labels:  async
Redux Most
Most.js based middleware for Redux. Handle async actions with monadic streams & reactive programming.
Stars: ✭ 137 (-1.44%)
Mutual labels:  async
Aiohttp
Asynchronous HTTP client/server framework for asyncio and Python
Stars: ✭ 11,972 (+8512.95%)
Mutual labels:  async

AsyncWith

Build Status

The asynchronous version of Elixir's with, resolving the dependency graph and executing the clauses in the most performant way possible!

Installation

Add async_with to your project's dependencies in mix.exs:

def deps do
  [{:async_with, "~> 0.3"}]
end

And fetch your project's dependencies:

$ mix deps.get

Usage

TL;DR: use AsyncWith and just write async in front of with.

async with always executes the right side of each clause inside a new task. Tasks are spawned as soon as all the tasks that it depends on are resolved. In other words, async with resolves the dependency graph and executes all the clauses in the most performant way possible. It also ensures that, if a clause does not match, any running task is shut down.

Let's start with an example:

iex> use AsyncWith
iex>
iex> opts = %{width: 10, height: 15}
iex> async with {:ok, width} <- Map.fetch(opts, :width),
...>            {:ok, height} <- Map.fetch(opts, :height) do
...>   {:ok, width * height}
...> end
{:ok, 150}

As in with/1, if all clauses match, the do block is executed, returning its result. Otherwise the chain is aborted and the non-matched value is returned:

iex> use AsyncWith
iex>
iex> opts = %{width: 10}
iex> async with {:ok, width} <- Map.fetch(opts, :width),
...>            {:ok, height} <- Map.fetch(opts, :height) do
...>  {:ok, width * height}
...> end
:error

In addition, guards can be used in patterns as well:

iex> use AsyncWith
iex>
iex> users = %{"melany" => "guest", "bob" => :admin}
iex> async with {:ok, role} when not is_binary(role) <- Map.fetch(users, "bob") do
...>   :ok
...> end
:ok

Variables bound inside async with won't leak; "bare expressions" may also be inserted between the clauses:

iex> use AsyncWith
iex>
iex> width = nil
iex> opts = %{width: 10, height: 15}
iex> async with {:ok, width} <- Map.fetch(opts, :width),
...>            double_width = width * 2,
...>            {:ok, height} <- Map.fetch(opts, :height) do
...>   {:ok, double_width * height}
...> end
{:ok, 300}
iex> width
nil

An else option can be given to modify what is being returned from async with in the case of a failed match:

iex> use AsyncWith
iex>
iex> opts = %{width: 10}
iex> async with {:ok, width} <- Map.fetch(opts, :width),
...>            {:ok, height} <- Map.fetch(opts, :height) do
...>   {:ok, width * height}
...> else
...>   :error ->
...>     {:error, :wrong_data}
...> end
{:error, :wrong_data}

If an else block is used and there are no matching clauses, an AsyncWith.ClauseError exception is raised.

Order-dependent clauses that do not express their dependency via their used or defined variables could lead to race conditions, as they are executed in separated tasks:

use AsyncWith

async with Agent.update(agent, fn _ -> 1 end),
           Agent.update(agent, fn _ -> 2 end) do
  Agent.get(agent, fn state -> state end) # 1 or 2
end

Check the documentation for more information.

Documentation

Documentation is available at https://hexdocs.pm/async_with

Code formatter

As described in Code.format_string!/2 documentation, Elixir will add parens to all calls except for:

  1. calls that have do/end blocks
  2. local calls without parens where the name and arity of the local call is also listed under :locals_without_parens

async with expressions should fall under the first category and be kept without parens, because they are similar to with/1 calls.

This is then the recommended .formatter.exs configuration:

[
  # Regular formatter configuration
  # ...

  import_deps: [:async_with]
]

As an alternative, you can add async: 1 and async: 2 directly to the list :locals_without_parens.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/fertapric/async_with. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

Running tests

Clone the repo and fetch its dependencies:

$ git clone https://github.com/fertapric/async_with.git
$ cd async_with
$ mix deps.get
$ mix test

Building docs

$ mix docs

Acknowledgements

I would like to express my gratitude to all the people in the Elixir Core Mailing list who gave ideas and feedback on the early stages of this project. A very special mention to Luke Imhoff (@KronicDeth), Theron Boerner (@hunterboerner), and John Wahba (@johnwahba).

Copyright and License

(c) Copyright 2017-2019 Fernando Tapia Rico

AsyncWith source code is licensed under the MIT License.

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].