在我的凤凰应用程序中,我有一个像Reviews.create(attributes)
这样的模块函数。它立即执行一些工作,并启动任务(使用Task.async
(以执行其他工作。
我可以这样测试它:
{:ok, new_review, task} = Reviews.create(attrs)
# verify that new_review is correct
Task.await(task)
# verify that the task did what it should
但是,我想使用Reviews.create/1
的主要位置是来自控制器。从那里开始,我想将任务视为"即发即忘"。这很简单:我只是不打电话给Task.await
.
这工作正常,除了当我测试控制器时,我收到以下错误:
[error] Postgrex.Protocol (#PID<0.344.0>) disconnected: **
(DBConnection.ConnectionError) owner #PID<0.753.0> exited
while client #PID<0.755.0> is still running with: shutdown
换句话说,它告诉我,当控制器测试结束时,任务被终止了。我不在乎。
有没有办法更明确地"即发即弃"?例如,如果我可以从控制器调用Task.forget_about(task)
,那就太好了,但这并不存在。
您需要改用Task.start/1
。它基本上以相同的方式运行,它只是在不同的进程中运行你的函数,但它不会以任何方式链接到生成它的进程。
直接来自文档
这仅在任务用于副作用(即对返回的结果不感兴趣(时使用,并且不应链接到当前进程。
使用 Task.Supervisor.async_nolink/2
Task.Supervisor.async_nolink/2
在这里做了我想要的:
启动可以等待的任务。
这意味着我可以在我愿意的测试中调用Task.await(task)
。
但是,这也意味着启动任务的过程"没有链接"。
这实际上很重要,测试失败告诉我这一点,尽管我没有意识到这一点。传入的请求由一个进程处理,该进程在发送响应后死亡。因此,如果后台作业链接到该进程,它也会死亡。
为此,我必须首先将其添加到我的监督树中:
supervisor(Task.Supervisor, [[name: MyApp.TaskSupervisor]])
name:
只是一个原子;这并不意味着我必须有一个这个名字的模块。
然后我能够打电话:
Task.Supervisor.async_nolink(
MyApp.TaskSupervisor,
fn -> do_whatever end
)