f# 在 list.iteri 中执行等待异步方法
f# perform await async methods in list.iteri
我在 F# 4.0 中有以下代码
let processEscalation escalationAction (escalationEvents:UpdateCmd.Record list) =
printf "%A" Environment.NewLine
printf "Started %A" escalationAction
escalationEvents
|> List.iter ( fun x ->
printf "%A" Environment.NewLine
printf "escalation %A for with action: %A" x.incident_id escalationAction
service.PostAction(new Models.Action(x.incident_id, escalationAction, "escalated"))
|> Async.AwaitTask
|> ignore)
let ComposeEscalation() =
let escalationlevels = ["ESC1 REACHED"; "ESC2 REACHED"; "ESC3 REACHED"]
escalationlevels
|> List.map getEscalationEvents
|> List.iteri (fun i x -> processEscalation escalationlevels.[i] x)
其中下一行是对 returns Task
的 C# 异步方法的调用
service.PostAction(new Models.Action(x.incident_id, escalationAction, "escalated"))
compose 升级方法调用 processEscalation 三次。
但是,第二个调用在第一个调用完成之前开始。
我如何确保最后一行 list.iteri 等待并按顺序处理它们?
也许 processEscalation 应该在异步计算表达式中?
Async.AwaitTask
的作用是 returns 一个 Async
计算,可用于等待任务完成。在您的情况下,您永远不会对它做任何事情,因此循环只会继续下一次迭代。
你想要这样的东西:
service.PostAction(new Models.Action(x.incident_id, escalationAction, "escalated"))
|> Async.AwaitTask
|> Async.RunSynchronously
|> ignore
这应该会达到您预期的效果,尽管肯定有更好、更可组合的方式来表达这种逻辑。
编辑:我的意思是这样的,与核心Async.Parallel
函数相对应:
module Async =
let sequential (asyncs: seq<Async<'t>>) : Async<'t []> =
let rec loop acc remaining =
async {
match remaining with
| [] -> return Array.ofList (List.rev acc)
| x::xs ->
let! res = x
return! loop (res::acc) xs
}
loop [] (List.ofSeq asyncs)
然后你可以按照这些思路做一些事情:
escalationEvents
// a collection of asyncs - note that the task won't start until the async is ran
|> List.map (fun x ->
async {
let task =
service.PostAction(new Models.Action(x.incident_id, escalationAction, "escalated"))
return! Async.AwaitTask task
})
// go from a collection of asyncs into an async of a collection
|> Async.sequential
// you don't care about the result, so ignore it
|> Async.Ignore
// now that you have your async, you need to run it in a way that makes sense
// in your context - Async.Start could be another option.
|> Async.RunSynchronously
这里的好处是,您没有将所有内容都捆绑到一个循环中,而是将计算分成了明确界定的阶段。它很容易遵循和重构(例如,如果您需要并行处理这些事件,您只需切换管道中的一个步骤)。
我在 F# 4.0 中有以下代码
let processEscalation escalationAction (escalationEvents:UpdateCmd.Record list) =
printf "%A" Environment.NewLine
printf "Started %A" escalationAction
escalationEvents
|> List.iter ( fun x ->
printf "%A" Environment.NewLine
printf "escalation %A for with action: %A" x.incident_id escalationAction
service.PostAction(new Models.Action(x.incident_id, escalationAction, "escalated"))
|> Async.AwaitTask
|> ignore)
let ComposeEscalation() =
let escalationlevels = ["ESC1 REACHED"; "ESC2 REACHED"; "ESC3 REACHED"]
escalationlevels
|> List.map getEscalationEvents
|> List.iteri (fun i x -> processEscalation escalationlevels.[i] x)
其中下一行是对 returns Task
的 C# 异步方法的调用service.PostAction(new Models.Action(x.incident_id, escalationAction, "escalated"))
compose 升级方法调用 processEscalation 三次。 但是,第二个调用在第一个调用完成之前开始。 我如何确保最后一行 list.iteri 等待并按顺序处理它们? 也许 processEscalation 应该在异步计算表达式中?
Async.AwaitTask
的作用是 returns 一个 Async
计算,可用于等待任务完成。在您的情况下,您永远不会对它做任何事情,因此循环只会继续下一次迭代。
你想要这样的东西:
service.PostAction(new Models.Action(x.incident_id, escalationAction, "escalated"))
|> Async.AwaitTask
|> Async.RunSynchronously
|> ignore
这应该会达到您预期的效果,尽管肯定有更好、更可组合的方式来表达这种逻辑。
编辑:我的意思是这样的,与核心Async.Parallel
函数相对应:
module Async =
let sequential (asyncs: seq<Async<'t>>) : Async<'t []> =
let rec loop acc remaining =
async {
match remaining with
| [] -> return Array.ofList (List.rev acc)
| x::xs ->
let! res = x
return! loop (res::acc) xs
}
loop [] (List.ofSeq asyncs)
然后你可以按照这些思路做一些事情:
escalationEvents
// a collection of asyncs - note that the task won't start until the async is ran
|> List.map (fun x ->
async {
let task =
service.PostAction(new Models.Action(x.incident_id, escalationAction, "escalated"))
return! Async.AwaitTask task
})
// go from a collection of asyncs into an async of a collection
|> Async.sequential
// you don't care about the result, so ignore it
|> Async.Ignore
// now that you have your async, you need to run it in a way that makes sense
// in your context - Async.Start could be another option.
|> Async.RunSynchronously
这里的好处是,您没有将所有内容都捆绑到一个循环中,而是将计算分成了明确界定的阶段。它很容易遵循和重构(例如,如果您需要并行处理这些事件,您只需切换管道中的一个步骤)。