首页 > 解决方案 > 如何在 Elixir 中编写多事件驱动的 if 子句?

问题描述

我需要指定一种适合高并发应用程序的语言(用于工业自动化目的,与分布式在线游戏有类似的要求)。

认为一个案例c取决于两个变量ab。我们的代码是这样的(在 Javascript 中):

if (a && b){
  c();
}

如果a由不同的事件设置,则代码变得有点复杂:

on('a', function(a){
  if (a && b){
    c();
  }
});

如果两者都由两个不同ab事件设置,事情会变得更加复杂。一种解决方案可能是使用信号库:

branch = new SignalBranch()
signal1 = branch.add()
on('a', function(a){
  if (a) {
    signal1.go()
  }
})
signal2 = branch.add()
on('b', function(b){
  if (b) {
    signal2.go()
  }
})
branch.joined(function(err, signals){
  c()
})

...另一个是使用Promise:(取自本网站的示例)

var signal1 = new Promise(function(resolve){
  on('a', function(a){
    if (a) {
      resolve()
    }
  })
})
var signal2 = new Promise(function(resolve){
  on('b', function(b){
    if (b) {
      resolve()
    }
  })
})
Promise.all([signal1, signal2]).then(function(data) {
  c();
});

正如所见,实现(因此可读性)变得越来越复杂,使得代码更难阅读。

如何在 Elixir 中以干净的方式实现相同的任务(通过和信号触发c事件)?ab

标签: javascriptconcurrencyelixir

解决方案


这实际上取决于您的用例。但这是一种可能的解决方案。

您可以使用:gen_eventa和发出信号b。然后,您可以让一些GenServer负责操作的人员c监听发出的事件。一旦您GenServer收到关于ab完成的两条消息,您就可以执行您的操作c。您的代码如下所示。

defmodule EventTest.EventManager do
  def notify(event) do
    :gen_event.notify(__MODULE__, event)
  end

  def register(ref) do
    :gen_event.add_handler(__MODULE__, ref, [])
  end
end

defmodule EventTest.EventConsumer do
  @behaviour :gen_event

  def init(_) do
    state = %{
      a: false,
      b: false
    }

    {:ok, state}
  end

  def maybe_c(%{a: true, b: true}) do
    # Do the thing
    IO.puts "C COMPLETED!"
  end
  def maybe_c(_), do: nil

  def handle_event(event, state) do
    new_state = %{ state | event => true }

    maybe_c(new_state)

    {:ok, new_state}
  end

  def handle_call(msg, state) do
    IO.inspect msg, label: "Unexpected Message"
    {:ok, :ok, state}
  end
end

然后在某个地方你只需要注册EventTest.EventConsumerEventTest.EventManager. 调用EventTest.EventManager.notify(:a)and之后EventTest.EventManager.notify(:b)(以任意顺序),您将看到执行c操作的输出。在这种情况下,它只是打印到屏幕上。

这样做的好处是,如果您需要在某个任意时间范围内完成操作以完成操作a,您可以根据需要向自己发送消息以重置或键。bc:a:b


推荐阅读