GenServer 及其状态
GenServer and its state
我这个简单的 genserver:
def handle_info(:tick, items) do
items2 = do_job(items)
tick()
{:noreply, items2}
end
在 "do_job" 中,我需要 a) 遍历项目,b) 发出一个可能需要很长时间的 http 请求,c) 根据响应,更新数据库并通过删除当前项目来完成它来自 "items" 或仅更新数据库:
def do_job(items) do
Enum.each(items, fn(a) -> # or Enum.map
Task.start fn ->
case external_request(a) do
{:terminate, data} ->
Repo.update(....)
remove(a) # send a message to this GenServer to remove itself
{:continue, data} ->
Repo.update(....)
end
end
end)
end
1) 我是否 必须 return 一个新值 -- 更新 list/state -- 从 "do_job" 允许我的 GenServer 工作合适吗?
2) 如果可以,怎么做?我无法从 "do_job" return 更新状态,因为我为每个项目创建了一个任务,因为它需要在数据库中发送 http 请求和 CRUD 操作。这就是异步任务的原因。
3) 一般来说, GenServer 自己管理状态变量,在本例中是 "items"。是什么让 GenServer 了解如何更新它?让我们考虑一下这段代码:
def add(a) do
GenServer.cast(__MODULE__, {:add, a}) # what/who utilizes this return value?
end
def remove(....) do
# ....
def handle_cast({:add, a}, items) do
{:noreply, [a | items]} # what/who utilizes this return value?
end
客户端不使用来自"add"或"remove"的returned值,因此它被丢弃了。尽管如此,当客户端调用 "add" 3 次时,此 GenServer 将在列表中包含 3 个项目。但为什么? GenServer 如何处理?
1) Do I have to return a new value -- updated list/state -- from "do_job" to allow my GenServer work properly?
您没有到return来自handle_info
的新更新列表,但如果您return相同的列表,下一条 :tick
消息可能会导致对相同的项目发出一组新的请求,即使他们的第一个 运行 最终会 return {:terminate, _}
如果您通过发送来处理删除作业do_job
稍后发给此 GenServer 的消息。
2) If so, how? I can't return an updated state from "do_job" because I create a Task for each item because it requires sending an http request and CRUD operation in a database. That's why an asyncronous task.
您可以 return 新列表,同时 运行 并行处理作业。这可以通过首先生成所有任务,然后等待所有任务来完成。我肯定会建议这种方式,因为它简单有效地防止重复工作 运行.
这是此方法的一个实现(未经测试):
def do_job(items) do
items
|> Enum.map(fn(a) ->
Task.async(fn ->
case external_request(a) do
{:terminate, data} ->
Repo.update(....)
[] # remove this item from list
{:continue, data} ->
Repo.update(....)
[a] # keep this item in list
end
end)
end)
|> Enum.flat_map(&Task.await/1)
end
do_job
现在将获取项目列表,将所有项目并行传递给 external_request
,然后等到所有项目都已 returned,然后删除所有项目来自 {:terminate, _}
案例的列表。
您可以保持 handle_info
实现与以前相同。
我这个简单的 genserver:
def handle_info(:tick, items) do
items2 = do_job(items)
tick()
{:noreply, items2}
end
在 "do_job" 中,我需要 a) 遍历项目,b) 发出一个可能需要很长时间的 http 请求,c) 根据响应,更新数据库并通过删除当前项目来完成它来自 "items" 或仅更新数据库:
def do_job(items) do
Enum.each(items, fn(a) -> # or Enum.map
Task.start fn ->
case external_request(a) do
{:terminate, data} ->
Repo.update(....)
remove(a) # send a message to this GenServer to remove itself
{:continue, data} ->
Repo.update(....)
end
end
end)
end
1) 我是否 必须 return 一个新值 -- 更新 list/state -- 从 "do_job" 允许我的 GenServer 工作合适吗?
2) 如果可以,怎么做?我无法从 "do_job" return 更新状态,因为我为每个项目创建了一个任务,因为它需要在数据库中发送 http 请求和 CRUD 操作。这就是异步任务的原因。
3) 一般来说, GenServer 自己管理状态变量,在本例中是 "items"。是什么让 GenServer 了解如何更新它?让我们考虑一下这段代码:
def add(a) do
GenServer.cast(__MODULE__, {:add, a}) # what/who utilizes this return value?
end
def remove(....) do
# ....
def handle_cast({:add, a}, items) do
{:noreply, [a | items]} # what/who utilizes this return value?
end
客户端不使用来自"add"或"remove"的returned值,因此它被丢弃了。尽管如此,当客户端调用 "add" 3 次时,此 GenServer 将在列表中包含 3 个项目。但为什么? GenServer 如何处理?
1) Do I have to return a new value -- updated list/state -- from "do_job" to allow my GenServer work properly?
您没有到return来自handle_info
的新更新列表,但如果您return相同的列表,下一条 :tick
消息可能会导致对相同的项目发出一组新的请求,即使他们的第一个 运行 最终会 return {:terminate, _}
如果您通过发送来处理删除作业do_job
稍后发给此 GenServer 的消息。
2) If so, how? I can't return an updated state from "do_job" because I create a Task for each item because it requires sending an http request and CRUD operation in a database. That's why an asyncronous task.
您可以 return 新列表,同时 运行 并行处理作业。这可以通过首先生成所有任务,然后等待所有任务来完成。我肯定会建议这种方式,因为它简单有效地防止重复工作 运行.
这是此方法的一个实现(未经测试):
def do_job(items) do
items
|> Enum.map(fn(a) ->
Task.async(fn ->
case external_request(a) do
{:terminate, data} ->
Repo.update(....)
[] # remove this item from list
{:continue, data} ->
Repo.update(....)
[a] # keep this item in list
end
end)
end)
|> Enum.flat_map(&Task.await/1)
end
do_job
现在将获取项目列表,将所有项目并行传递给 external_request
,然后等到所有项目都已 returned,然后删除所有项目来自 {:terminate, _}
案例的列表。
您可以保持 handle_info
实现与以前相同。