How do I block until an anonymous function is called?

时间:2016-08-31 16:57:49

标签: elixir

I'm using a library that does an asynchronous request (push) that always returns ok. It calls a callback with the actual response, whether ok or an error.

callback = fn(response) ->
  # handle error or ok
end

:ok = Pigeon.APNS.push(n, callback)
# push returns immediately, but I want to block
# until callback is called and return that response

In this case I want to block and wait for the callback, turning this into a synchronous request. Is there an OTP feature like Task.await or some other solution to turn this into a synchronous request?

2 个答案:

答案 0 :(得分:3)

You can send a message from the callback to parent process and use receive like this:

defmodule Pigeon.APNS do
  def push(_n, callback) do
    spawn_link(fn ->
      :timer.sleep(2000)
      callback.(:ok)
    end)
    :ok
  end
end

ref = make_ref()
pid = self()

callback = fn(_response) ->
  IO.puts "executing callback"
  send(pid, ref)
end

:ok = Pigeon.APNS.push(0, callback)

IO.puts "called push"

receive do
  ^ref -> :ok
end

IO.puts "callback called"

Output:

called push
executing callback
callback called

答案 1 :(得分:0)

您可以发送消息,然后等待回调之外的消息继续,而不是从回调中返回。确保邮件包含唯一引用,并且您正在等待来自该引用的邮件,否则您将无法知道邮件的来源。

# Create a unique reference so you know
# where the return is coming from
id = make_ref()

# A function for waiting until a value
# comes in from a reference
wait_for_response = fn (id) ->
  receive do
    {^id, val} -> val
  end
end

callback = fn (val) ->
  IO.puts("In Callback")
  # instead of returning, send the value to self with
  # the right reference
  send(self, {id, "I was passed the argument #{inspect val}"})
end

# Async fun knows nothing about this diferment
# and functions as normal
async_fun = fn (i, cb) ->
  IO.puts("In Async Function")
  Process.sleep(2000)
  cb.(i)
end

# Usage
IO.puts("Starting")
async_fun.(1, callback)
val = wait_for_response.(id)
IO.puts "Callback returned: #{inspect val}"
IO.puts("All Done")