我们假设我有一个函数main_function
取决于其他三个函数的结果,每个函数都可以返回{:ok, result}
或{:error, error}
。我怎样才能避免在javascript中使用深度嵌套的case语句,感觉就像回调地狱。
示例:
def func1(input) do
case SOMECONDITION do
{:ok, result} ->
{:ok, result}
{:error, error} ->
{:error, error}
end
end
def func2(input) do
case SOMECONDITION do
{:ok, result} ->
{:ok, result}
{:error, error} ->
{:error, error}
end
end
def func3(input) do
case SOMECONDITION do
{:ok, result} ->
{:ok, result}
{:error, error} ->
{:error, error}
end
end
def main_function(input) do
case func1(input) do
{:ok, result} ->
case func2(result) do
{:ok, result} ->
case func3(result) do
{:ok, result} ->
{:ok, EXPECTED_OUTCOME}
{:error, error} ->
{:error, error}
end
{:error, error} ->
{:error, error}
end
{:error, error} ->
{:error, error}
end
end
end
这感觉不对......
答案 0 :(得分:15)
[编辑]:我为一个涵盖这个概念的精彩演讲添加了一个便利链接,以及来自下面ElixirConf 2018的更复杂需求的解决方案。
别担心 - Elixir让你满意。您想要this special form:with/1
with/1
才会继续执行函数。
你的主要功能基本上看起来像:
def main_function(input) do
with {:ok, result_1} <- func1(input),
{:ok, result_2} <- func2(result_1),
...,
do: {:ok, EXPECTED_OUTCOME}
end
当它找不到匹配时,比如因为有一个像{:error, _error}
这样的元组,特殊形式将返回遇到的第一个错误并停止执行这些函数。
您还可以添加其他条件。我使用过这个例子的例子是当用户可能期望某些操作需要完成许多功能时,我想提醒他们同样的事情,无论它在哪里失败:
def main_function(input) do
with {:ok, result_1} <- func1(input),
{:ok, result_2} <- func2(result_1),
... do
{:ok, EXPECTED_OUTCOME}
else
_error ->
{:error, "Couldn't complete action"}
end
end
以下是来自Credo作者关于此概念的精彩演讲,由ElixirConf 2018提供:https://www.youtube.com/watch?v=ycpNi701aCs&t=473s
以下是with/1
上的精彩帖子:https://www.erlang-solutions.com/blog/exploring-with-the-elixir-special-form.html
答案 1 :(得分:2)
回答对已接受答案的评论:
这是一种有趣的方法。但是,让我们说
func1
和func2
正在写入数据库。如果func1
返回错误,我根本不想执行func2
。会是这种情况吗?
如果您使用 Ecto ,还有其他选择。
让我们说func1
,func2
和func3
所有写入数据库,但步骤在func2
处失败,表示func1
已写入数据库。
让他们说他们返回{:ok, result}
或{:error, error}
。
Ecto.Multi
救援!
alias Ecto.Multi
alias MyApp.Repo
def main_function(input) do
result =
Multi.new()
|> Multi.run(:result1, fn _ -> func1(input) end)
|> Multi.run(:result2, &func2(&1.result1))
|> Multi.run(:result3, &func3(&1.result2))
|> Repo.transaction()
case result do
{:ok, %{result1: _, result2: _, result3: _}} ->
{:ok, EXPECTED_OUTCOME}
{:error, error, _changes_so_far} ->
{:error, error}
end
end
使用Multi,返回错误元组将中止任何进一步的操作并使整个多次失败。此外,由于它使用了一个事务,任何成功的事都将被回滚。