I’m looking for a “standard” way to have the Task
Elixir module in a pure Erlang solution.
Do you know if there is already a library that implement (backport ) it?
Hi,
I am not aware of such an erlang library.
But since your profile states that you are just getting into erlang, it might be a nice task (pun not intended) to look at
and try to port it to erlang.
At least for me, I learn the most if I hack along myself.
Hi,
I am not aware of such a library but code in native Erlang using spawn() function is pretty short and clean.
Elixir (from previous response):
...
task = Task.async(fn -> do_some_work() end)
res = do_some_other_work()
res + Task.await(task)
...
Erlang:
====== do_work.erl ============
-module(do_work).
-export([start/0]).
% Define a function to do the work with async task
start() ->
% Save process id (pid) of the current process
MainPid = self(),
io:format("main pid: ~p~n", [MainPid]),
spawn( % Spawn a new process to start a task function (this is "Async" part)
fun() -> % "fun() -> ... end" defines a lambda function (unnamed function)
% that is executed in the async task process
io:format("task pid: ~p~n", [self()]), % here self() has different value <> MainPid
% because its called in a different process
Result = do_some_work(), % do work
MainPid ! {result, Result} % after finishing work send a result to the mailbox
% of process with MainPid (function that spawned this function)
% (lanbda function body commands have access to variables
% in the context of caller function, in our case - MainPid)
end
),
% Perform some other work in the main process (not waiting finishing of spawned task)
OtherResult = do_some_other_work(),
% Wait for the spawned process to send back the result (this is "Await" part)
receive % wait for response message
{result, TaskResult} -> % message received
io:format("result: ~p~n", [OtherResult ++ " " ++ TaskResult])
after 5000 -> % after 5 seconds (if result not received)
io:format("timeout~n", [])
end.
% Example implementations of the work functions
do_some_work() ->
% Some work
"R2". % return result
do_some_other_work() ->
% Some other work
"R1". % return result
...
Hi,
that’s a good starting point. Depending on how much of the Elixir.Task [1] functionality the OP
needs, there is some more to do.
For example await() returns the return value of the function of the task. With a plain spawn() you do not get the return value.
You could implement this with a wrapper around the function which sends the return value up to the parent. Or, if you do not want to implement all the boiler plate code yourself, use a gen_server as wrapper and use a gen_server:call().
And a very nice feature IMO is await_many() which waits for the returns of many tasks and delivers their return values in the order of the tasks. This can be nicely integrated into a functional construct like map.
[edit] I started an implementation in erlang in the company, if I find it, I could post it next week.
Hi, there is no equiv that I am aware. You’ve gotten some good answers here but I wanted to respond so I could lay out some of the differences between Task in elixir and just using spawn.
Key differences :
- Tasks are supervised, options are specifically threaded through to DynamicSupervisor to spin up the proc. This is a big difference between just using spawn and using Task to be aware of.
- Similarly and quite related, some cases are handled for you when using Task, that you will have to handle yourself when just using spawn, such as an exit from the process.
IMO any worker pool manager is much better, even the simplest worker_pool from rabbit_common.