Прерывание процесса в случае сбоя

#elixir

#elixir

Вопрос:

Я разрабатываю API в elixir, и у меня есть processo, выполняющий 6 действий подряд. Мой вопрос в том, как я могу прервать это выполнение в случае сбоя в любом из этих 6 действий?

Я бы не хотел разбивать каскад на несколько блоков case.

На самом деле я бы хотел что-то вроде «return».

Есть ли у кого-нибудь хорошее решение для этого?

Комментарии:

1. Можете ли вы показать этот код или его представительный эквивалент?

Ответ №1:

Когда у вас есть ряд последовательных условных выражений в Elixir (например, несколько блоков case), with предложение (оператор? оператор?) может быть полезным.

В документах Elixir есть пример.

Сравните вонючий беспорядок вложенных операторов case, подобных этому:

 case op1(x) do
  {:ok, result1} -> 
     case op2(result1) do
        {:ok, result2} -> 
            case op3(result2) do
               {:ok, result3} -> {:ok, result3}
               {:error, error} -> {:error, error}      
            end
        {:error, error} -> {:error, error}      
     end   
  {:error, error} -> {:error, error}      
end
 

Это действительно сложно понять, и {:error, error} -> {:error, error} биты кажутся избыточными. По сути, мы просто хотим продолжить выполнение последовательных операций (1, 2, 3, …) или завершить работу с ошибкой.

Мы можем переписать выше, используя with синтаксис — обратите пристальное внимание на направление стрелок!

 with {:ok, result1} <- op1(x),
     {:ok, result2} <- op2(result1) do
  op3(result3)
else
  {:error, error} -> {:error, error}
end
 

или, более кратко, опустить else блок, если не требуется специальная обработка ошибок:

 with {:ok, result1} <- op1(x),
     {:ok, result2} <- op2(result1) do
  op3(result3)
end
 

Вы используете запятую для разделения одной операции за другой, и, наконец, кульминацией является do блок, в котором вы можете выполнить заключительную операцию.

Если какая-либо из операций завершается с ошибкой, выполнение может быть перенаправлено для сопоставления в необязательном else блоке — это наиболее полезно, если вам нужно различать разные возвращаемые значения (например, если требуется конкретное ведение журнала), в противном else случае блок является избыточным и часто может быть опущен.

Понимание with блока — большой шаг в написании более чистого кода Elixir. Альтернативой использованию with синтаксиса может быть написание нескольких сигнатур функций для обработки выходных данных каждого шага, но это кажется большой занятостью, которая не всегда оправдана.

Комментарии:

1. else блок в форме {:error, error} -> {:error, error} полностью избыточен. {:error, error} будет возвращен with/1 без него, после обнаружения.

2. Вы, конечно, правы — я обновил объяснение. Однако я считаю полезным иметь else блок в качестве примера, в случае, если вам нужно сопоставить разные возвращаемые значения.

Ответ №2:

Отсутствие возможности возврата действительно какое-то время путалось у меня в голове в elixir. Я рекомендую вам использовать сопоставление с образцом. Я знаю, что это кажется неправильным, но в конечном итоге вы поймете простоту, стоящую за этим. Вы можете сделать это без вложенности, просто создайте новую функцию для каждого ожидаемого шаблона.

Вот фрагмент, в котором я в основном повторяю self()

   ...

  def handle_cast({:get_name, port, container}, state) do
    app_name = Map.get(container, "Labels") |> Map.get("app")
    GenServer.cast(self(), {:domain_map, app_name, port, container})
    {:noreply, state}
  end

  def handle_cast({:containers, containers}, state) do
    Enum.map(containers, fn container ->
      GenServer.cast(self(), {:container, container})
    end)

    {:noreply, state}
  end

  def handle_cast({:container, container}, state) do
    Enum.map(Map.get(container, "Ports"), fn port ->
      GenServer.cast(self(), {:get_name, port, container})
    end)

    {:noreply, state}
  end

 ...
 

Если шаблон не соответствует, это в основном ваш оператор return .