Speaking in terms of promises, I want a server which creates a promise, returns it to a client, and resolves it sometimes in the future; a client would await/yield for result.
General schema:
# GenServer
def handle_call(:get_promise, from, promises) do
promise = Task.??? # can't say what task should return right now; it's value must be filled later by this module
{:reply, promise, [promises | promise], {:continue, :resolve_promise}}
end
def handle_continue(:resolve_promise, promises) do
... # resolve promises now or maybe later
end
# Client
promise = Server.get_promise()
Task.await(promise)
The thing is Task creator cannot provide a function returning a value because it will be available sometimes in the future (e.g. result is a response of external API call).
I’m struggling to do it with Task and Task.Supervisor and looks like I’m missing something. Or Task is missing some of Promises flexibility?
I come up with some ideas which I don’t like:
- Actually make “promise” resolving inside Task and send some message to
from
. Bad idea because a user of the module has to modify itself to be able to use the module. - Same but instead of sending message to client, return client a function creating a task. So the task will be linked to client process. Am I going to monitoring hell with this?
- Save resolving values into
state
and wait for them inside Task:
This looks bad too because it is verbose and smells with reinventing the wheel.def handle_call(:get_promise, from, responses_map) do promise_uuid = get_uuid_somewhere() promise = Task.Supervisor.asynk_nolink(MySup, fn -> # somehow wait for value in responses_map under key promise_uuid; I guess task would need to be a module to get `while loop` end) {:reply, promise, [promises | promise], {:continue, {:resolve_promise, promise_uuid}}} end
Also I cannot google why Task has to be owned by calling process and cannot be awaited by anyone (or cannot transfer ownership or change it upon creation).