2014-02-23 102 views
2

我有做一些IO與此簽名的方法:F#異步和匿名函數

member this.IsRestaurantInCatagoryAsync(restaurantName: string, restaurantAddress: string, restaurantCatagory: string) = 
    async { ///long running methods } 

我想調用它在一個匿名函數,像這樣:

this.GetRestaurants() 
     |> Seq.filter(fun (name, address) -> catagoryRepository.IsRestaurantInCatagoryAsync(name, address,catagory)) 
     |> Seq.toList 

的問題是, IsRestaurantInCatagoryAsync返回一個異步,而不是一個布爾。我如何讓Seq.Filter處理它?我應該使用let將異步投射到布爾!那麼我必須編寫一個非匿名函數來分配返回值?

回答

3

您可以使用Async.RunSynchronously以同步方式運行操作 - 但是這會破壞使用異步工作流程以避免編寫代碼的問題,因此這不是正確的做法!

有不同的方法可以做到這一點 - 你可以循序遍歷所有餐館(它將逐個處理),或者你可以並行運行過濾(這將使用盡可能多的線程池線程,因爲.NET發現很好)。

並行版本是這樣的:

let checkAll = async { 
    let! all = 
    [ for r in this.GetRestaurants() -> async { 
     let! include = catagoryRepository.IsRestaurantInCatagoryAsync(name, address,catagory) 
     if include then return Some(r) else return None } ] 
    |> Async.Parallel 
    let included = Seq.choose id all 
    printfn "%A" included } 

注意,代碼是所有裏面async塊(因爲這保持它異步)。它首先創建一個計算列表,用None(跳過餐廳)或Some(包括餐廳)返回選項,然後運行所有選項並使用Seq.choose過濾None值。

要實現這個順序,你基本上需要你自己的實現filter包裝在async塊。這將是一個很好的起點(雖然它不是尾遞歸):

let rec filterAsync f items = async { 
    match items with 
    | [] -> return [] 
    | x::xs -> 
     let! included = f x 
     let! rest = filterAsync f xs 
     return (if included then x::rest else rest) } 
+0

是否有任何計劃將Seq.FilterAsync等添加到F#規範中? –