You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Elixir中Task.await触发FunctionClauseError问题的解决求助

解决Task.async_stream配合Task.await触发FunctionClauseError的问题

问题重现

尝试通过子进程批量检查质数,调用Task.await时触发FunctionClauseError,核心代码及报错如下:

defmodule LeaderWorker do
  def start() do
    children = [{Task.Supervisor,
      name: Task.SomeThingSupervisor,
    }]

    Supervisor.start_link(children, strategy: :one_for_one)
  end

  def run(number_list, num_workers) do
    results =
      number_list
      |> Task.async_stream(&check_prime/1, max_concurrency: num_workers)
      |> Enum.map(&Task.await/1)

    IO.inspect(results)
  end

  def check_prime(number) do
    prime = is_prime(number)
    IO.inspect({number, prime})
    {number, prime}
  end

  def is_prime(number) when number <= 1 do
    false
  end

  def is_prime(number) do
    not Enum.any?(2..(div(number, 2)), &(&1 != 1 and rem(number, &1) == 0))
  end
end

LeaderWorker.start()
number_list = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
LeaderWorker.run(number_list, 4)

报错信息:

elixir .\clean.ex
{2, false}
{3, true}
{4, false}
{5, true}
** (FunctionClauseError) no function clause matching in Task.await/2    

    The following arguments were given to Task.await/2:

        # 1
        {:ok, {2, false}}

        # 2
        5000

    Attempted function clauses (showing 1 out of 1):

        def await(%Task{ref: ref, owner: owner} = task, timeout) when timeout == :infinity or is_integer(timeout) and timeout >= 0

    (elixir 1.16.2) Task.await/2
    (elixir 1.16.2) lib/enum.ex:1708: anonymous fn/3 in Enum.map/2
    (elixir 1.16.2) lib/enum.ex:4396: anonymous fn/3 in Enum.map/2
    (elixir 1.16.2) lib/task/supervised.ex:386: Task.Supervised.stream_deliver/7
    (elixir 1.16.2) lib/enum.ex:4396: Enum.map/2
    clean.ex:14: LeaderWorker.run/2
    clean.ex:36: (file)

错误原因

Task.async_stream的返回值是一个包含结果元组的流,每个元素格式为{:ok, result}(任务失败时为{:error, reason}),而Task.await要求接收的是Task结构体,直接将{:ok, result}传给Task.await必然不匹配函数子句,触发错误。

同时原代码的is_prime函数存在逻辑错误:对于数字2,当前实现会错误返回false,需要修正质数判断逻辑。

修复后的代码

defmodule LeaderWorker do
  def start() do
    children = [{Task.Supervisor, name: Task.SomeThingSupervisor}]
    Supervisor.start_link(children, strategy: :one_for_one)
  end

  def run(number_list, num_workers) do
    results =
      number_list
      |> Task.async_stream(&check_prime/1, max_concurrency: num_workers)
      # 直接提取async_stream返回的结果,无需调用Task.await
      |> Enum.map(fn {:ok, result} -> result end)

    IO.inspect(results)
  end

  def check_prime(number) do
    prime = is_prime(number)
    {number, prime}
  end

  def is_prime(number) when number <= 1, do: false
  def is_prime(2), do: true # 单独处理最小质数2
  def is_prime(number) when number even?, do: false # 偶数直接返回false(除2外)
  def is_prime(number) do
    # 优化判断范围到平方根,提升大数字的计算性能
    max_divisor = :math.sqrt(number) |> floor()
    not Enum.any?(3..max_divisor//2, &(rem(number, &1) == 0))
  end
end

LeaderWorker.start()
number_list = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
LeaderWorker.run(number_list, 4)

关键修复点

  1. 移除多余的Task.await调用:Task.async_stream内部已经完成任务等待和结果收集,直接从{:ok, result}元组中提取结果即可。
  2. 修正质数判断逻辑:
    • 单独处理数字2,确保返回正确的true
    • 偶数(除2外)直接返回false,减少无效计算
    • 将判断范围缩小到数字的平方根,大幅提升大数字的质数判断效率

运行修复后的代码,会输出正确的结果列表:

[{2, true}, {3, true}, {4, false}, {5, true}, {6, false}, {7, true}, {8, false}, {9, false}, {10, false}, {11, true}]

内容的提问来源于stack exchange,提问作者Kiubert

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 10:04:53