2016-12-30 28 views
4

我在下面的代碼F#4.0F#執行等待在異步方法list.iteri

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) 

其中下面的行是該返回任務

service.PostAction(new Models.Action(x.incident_id, escalationAction, "escalated")) 

一個C#異步方法的調用組合升級方法調用了三次processEscalation。 但是,第二個呼叫在第一個呼叫完成之前開始。 如何確保最後一行list.iteri等待並按順序處理它們? 也許processEcalation應該是一個異步計算表達式?

+0

爲什麼'processEscalation'只等待任務'忽略'呢? –

+0

'processEscalation'用於製作web服務請求,但它返回任務。對於這個特定的腳本,我只是將其稱爲副作用,對返回值不感興趣。對不起,我對函數式編程比較陌生,所以我認識到這可能不是最好的方法。 – Chinwobble

回答

4

Async.AwaitTask所做的是返回可用於等待任務完成的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 

的好處在這裏不是將所有東西都捆綁到一個單一的循環中,而是將計算劃分爲劃分良好的階段。這很容易遵循並重構(例如,如果您需要並行處理這些事件,則只需切換管道中的一個步驟即可)。

+0

謝謝你的工作!此方法是否阻塞線程?你能提出一個更優雅的模式嗎?會使用代理(mailboxProcessor)更合適嗎? – Chinwobble

+2

'RunSynchronously'在當前線程上執行'async'塊,在你的情況下有效地阻止它(因爲你想等待任務完成)。 – scrwtp

+1

郵箱處理器將是一個概念上的矯枉過正,但我​​想建議將類似的原理操作 - 一個原語遞歸遍歷asyncs'的'序列,並收集結果,就像一個郵箱處理器的循環將遞歸調用本身。 – scrwtp