Jobbit
Run risky tasks asynchronously or synchronously without endangering the calling process.
Jobbit
is a thin, mildly opinionated wrapper for Elixir's Task
and
Task.Supervisor
modules and functionality.
Task
and Task.Supervisor
provide an easy-to-use, extensible, and
dependable interface for running one or many supervised or unsupervised
asynchronous tasks. If you want to "harness the power of OTP" you should
investigate those two modules and what can be achieved with their use.
Installation
Add jobbit
to your list of dependencies in mix.exs
:
def deps do
[
{:jobbit, "~> 0.5.0"},
]
end
Usage / Running Tasks
Tasks in Jobbit
can be run with a closure:
Jobbit.async(fn -> :ok end)
=> %Jobbit{}
Or with a module
, func
, and args
(similar to apply/3
):
Jobbit.async_apply(Kernel, :div, [1, 0])
=> %Jobbit{}
A task can be synchronized:
task = Jobbit.async(fn -> MyClient.send_request(payload) end)
=> %Jobbit{}
task
|> Jobbit.yield(2000) # yield with a custom timeout
|> case do
{:ok, %SomeResponse{}} -> :request_succeeded
{:error, :not_authorized} -> :request_returned_an_error
{:error, %TaskError{}} -> :request_crashed
{:error, %TimeoutError{}} -> :request_timeout
end
FAQ
Jobbit
like Task
?
How is -
Both are used to perform asynchronous tasks.
-
Both have
yield/2
which waits for results for a certain amount of time (timeout
), but does not raise upon timeout.
Jobbit
like Task.Supervisor
?
How is -
Both are used to perform asynchronous tasks.
-
Both can start caller-unlink, supervised tasks.
-
Both require a
Task.Supervisor
to be running-
Note:
Jobbit
itself can be used as a child_spec callback module instead ofTask.Supervisor
. -
Note:
Jobbit
starts its own task supervisor by default at application startup.
-
Jobbit
different than Task
?
How is -
Jobbit
never links to the calling process. All the risk is move to the task process. -
Jobbit
only provides one function to (idiomatically) synchronize on a tasks result (viayield/2
).Task
has also hasyield/2
which is similar, but also providesawait/2
which will raise if the task times out;yeild/2
will not raise. -
Jobbit
homogenizes results of tasks. WithTask
yielding can return{:ok, :ok}
.Jobbit
homogenizes{:ok, :ok}
into:ok
. This way is much less boilerplate.
Jobbit
different than Task.Supervisor
?
How is -
Jobbit
provides a default supervisor via its application tree. -
Jobbit
is less generalized, but easier to out-of-the-box. -
Jobbit
has fewer functions, and a more focused scope.Jobbit
ONLY runs asynchronous, unlinked tasks.
Task Supervision
With Jobbit
, tasks are run on a Jobbit
task supervisor and the
Jobbit.Application
starts a default task supervisor (default:
Jobbit.DefaultTaskSupervisor
) at application startup.
Jobbit
implements child_spec/1
and can, therefore, be used as
a child's callback module for a supervisor
A supervisor can be added to a supervision tree using like so:
# in `MyApp.SomeSupervisor` or in `MyApp.Application`...
# Note: it's a good idea to `:name` your task supervisor
# (because you need to be able to address it)...
children = [
{Jobbit, name: MyApp.MyBusinessDomainTaskSupervisor}
]
A custom Jobbit
task supervisor can also be started directly via
Jobbit.start_link/1
.
Jobbit.start_link(name: :some_task_sup)
=> {:ok, #PID<0.109.0>}
Or Jobbit.start_link/0
:
Jobbit.start_link()
=> {:ok, #PID<0.110.0>}
Configuration
Jobbit can be configured via config/*.exs
files.
By default, the :jobbit
OTP app will start a default
task supervisor called Jobbit.DefaultTaskSupervisor
.
The default task supervisor can be configured via the :default_supervisor
config value.
Additionally, the entire :jobbit
application can instructed not
to start by flagging :start_jobbit?
with a falsey (nil
or false
)
value.
Note: Jobbit.async/1
and Jobbit.async_apply/3
rely on the default supervisor
to be running when they are called. If start_jobbit?: false
is set in the config
and the :default_supervisor
is not set to a running task supervisor these
functions will not work.
An example of configuring :jobbit
:
config :jobbit,
start_jobbit?: true,
default_supervisor: Jobbit.DefaultTaskSupervisor,
default_supervisor_opts: []