nathany
nathany

Reputation: 549

How do I block until an anonymous function is called?

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?

Upvotes: 1

Views: 484

Answers (2)

greggreg
greggreg

Reputation: 12085

Instead of returning from the callback you can send a message and then wait for the message outside the callback to continue. Make sure the message contains a unique reference and you are waiting for a message from that reference otherwise you won't know where the message is coming from.

# 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")

Upvotes: 0

Dogbert
Dogbert

Reputation: 222128

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

Upvotes: 3

Related Questions