共用方式為


異步表達式

本文說明 F# 對於異步表達式的支援。 異步表達式提供以異步方式執行計算的一種方式,也就是不封鎖其他工作的執行。 例如,異步計算可用來撰寫具有使用者界面 (UI) 的應用程式,這些應用程式在執行其他工作時仍能回應使用者。 F# 異步工作流程程式設計模型 可讓您撰寫功能程式,同時隱藏連結庫中線程轉換的詳細數據。

您也可以使用 任務表達式撰寫非同步程式碼,直接建立 .NET 任務。 當與建立或使用 .NET 任務的 .NET 程式庫廣泛互操作時,最好使用任務表達式。 在 F# 中撰寫大部分異步程式代碼時,最好使用 F# 異步運算式,因為它們更簡潔、更組合,並避免與 .NET 工作相關聯的某些注意事項。

語法

async { expression }

備註

在先前的語法中,expression 所代表的計算會設定為異步執行,也就是說,在異步睡眠作業、I/O 和其他異步操作執行時,不會封鎖目前的計算線程。 異步計算通常會在背景線程上啟動,同時在目前線程上繼續執行。 表達式的類型為 Async<'T>,其中 'T 是使用 return 關鍵詞時表達式所傳回的類型。

Async 類別提供支持數個案例的方法。 一般方法是建立 Async 物件,代表您想要以異步方式執行的計算或計算,然後使用其中一個觸發函式啟動這些計算。 您使用的觸發程式取決於您是否要使用目前線程、背景線程或 .NET 工作物件。 例如,若要在目前線程上啟動異步計算,您可以使用 Async.StartImmediate。 當您從 UI 線程啟動異步計算時,不會封鎖處理使用者動作的主要事件迴圈,例如按鍵和滑鼠活動,因此您的應用程式會保持回應。

使用 let! 進行異步系結

在異步表達式中,某些表達式和作業是同步的,有些是異步的。 當您以異步方式呼叫 方法時,請使用 let!,而不是一般 let 系結。 let! 的效果是使得在執行計算時,可以同時進行其他計算或執行線程。 當 let! 系結的右側傳回結果後,異步表達式的其他部分會繼續執行。

下列程式代碼顯示 letlet!之間的差異。 使用 let 的程式代碼行只會建立異步計算作為物件,以便稍後使用 Async.StartImmediateAsync.RunSynchronously來執行。 使用 let! 的程式代碼行會啟動計算並執行異步等候:線程會暫停,直到結果可用為止,此時會繼續執行。

// let just stores the result as an asynchronous operation.
let (result1 : Async<byte[]>) = stream.AsyncRead(bufferSize)
// let! completes the asynchronous operation and returns the data.
let! (result2 : byte[])  = stream.AsyncRead(bufferSize)

let! 只能用來直接等待 F# 非同步計算 Async<T>。 您可以間接等候其他類型的異步操作:

  • 結合 Async.AwaitTask、.NET 工作中的 Task<TResult> 和非泛型的 Task
  • .NET 值工作、ValueTask<TResult> 和非泛型 ValueTask,透過結合 .AsTask()Async.AwaitTask
  • F# RFC FS-1097中指定的“GetAwaiter”模式之後的任何物件,結合 task { return! expr } |> Async.AwaitTask

控制流

異步表達式可以包含控制流程建構,例如 for .. in .. dowhile .. dotry .. with ..try .. finally ..if .. then .. elseif .. then ..。 接著,這些可能會進一步包含異步建構,除了同步執行的 withfinally 處理程式外。

F# 非同步表示式不支援非同步 try .. finally ..。 您可以針對此情況使用 工作運算式

useuse! 系結

在異步表達式中,use 系結可以系結至類型 IDisposable的值。 針對後者,會以異步方式執行處置清除作業。

除了 let!之外,您還可以使用 use! 來執行異步系結。 let!use! 之間的差異與 letuse之間的差異相同。 針對 use!,物件會在目前範圍結束時被處置。 請注意,在 F# 的目前版本中,use! 不允許將值初始化為 null,即使 use 也是如此。

異步原語

執行單一異步工作並傳回結果的方法稱為 異步基本類型,而這些方法專為搭配 let!使用而設計。 F# 核心連結庫中定義了數個異步基本類型。 有兩種此類方法適用於 Web 應用程式,定義於模組 FSharp.Control.WebExtensionsWebRequest.AsyncGetResponseHttpClient.GetStringAsync(封裝於 Async.AwaitTask,以相容於 F# 的異步模型)。 這兩種工具都會在提供URL後從網頁下載資料。 AsyncGetResponse 會產生 System.Net.WebResponse 物件,而 GetStringAsync 會產生代表網頁 HTML 的字串。

異步 I/O 作業的數個基本類型會包含在 FSharp.Control.CommonExtensions 模組中。 System.IO.Stream 類別的擴充方法包括 Stream.AsyncReadStream.AsyncWrite

您也可以透過定義主體為異步表達式的函式或方法,撰寫自己的異步原語。

若要在 .NET Framework 中使用針對其他異步模型設計的異步方法搭配 F# 異步程序設計模型,您可以建立會傳回 F# Async 物件的函式。 F# 連結庫具有函式,可讓您輕鬆執行此動作。

此處包含一個使用異步表達式的範例;異步類別方法的文件中還有其他許多。

此範例示範如何使用異步表達式平行執行程序代碼。

在下列程式代碼範例中,函式 fetchAsync 取得從 Web 要求傳回的 HTML 文字。 fetchAsync 函式包含異步的程式代碼區塊。 當系結至異步基本類型的結果時,在此情況下會使用 AsyncDownloadStringlet!,而不是使用 let

您可以使用 函式 Async.RunSynchronously 執行異步操作,並等候其結果。 例如,您可以使用 Async.Parallel 函式與 Async.RunSynchronously 函式,平行執行多個異步操作。 Async.Parallel 函式會取得 Async 物件清單、設定每個 Async 工作物件以平行方式執行的程式代碼,並傳回代表平行計算的 Async 物件。 就像進行單一操作一樣,您呼叫 Async.RunSynchronously 以開始執行。

runAll 函式會以平行方式啟動三個異步表達式,並等候它們全部完成。

open System.Net
open Microsoft.FSharp.Control.WebExtensions
open System.Net.Http

let urlList = [ "Microsoft.com", "http://www.microsoft.com/"
                "MSDN", "http://msdn.microsoft.com/"
                "Bing", "http://www.bing.com"
              ]

let fetchAsync(name, url:string) =
    async {
        try
            let uri = new System.Uri(url)
            let httpClient = new HttpClient()
            let! html = httpClient.GetStringAsync(uri) |> Async.AwaitTask
            printfn "Read %d characters for %s" html.Length name
        with
            | ex -> printfn "%s" (ex.Message);
    }

let runAll() =
    urlList
    |> Seq.map fetchAsync
    |> Async.Parallel
    |> Async.RunSynchronously
    |> ignore

runAll()

另請參閱