Sdílet prostřednictvím


Asynchronní výrazy

Tento článek popisuje podporu v jazyce F# pro asynchronní výrazy. Asynchronní výrazy poskytují jeden způsob asynchronního provádění výpočtů, tj. bez blokování provádění jiné práce. Například asynchronní výpočty se dají použít k psaní aplikací, které mají uživatelská rozhraní, která reagují na uživatele, když aplikace provádí jinou práci. Programovací model asynchronních pracovních postupů jazyka F# umožňuje psát funkční programy při skrytí podrobností přechodu vlákna v knihovně.

Asynchronní kód lze také vytvořit pomocí výrazů úloh, které vytvářejí úlohy .NET přímo. Použití výrazů úloh se upřednostňuje při rozsáhlé spolupráci s knihovnami .NET, které vytvářejí nebo využívají úlohy .NET. Při psaní většiny asynchronního kódu v jazyce F# jsou upřednostňované asynchronní výrazy jazyka F#, protože jsou stručnější, kompozitivnější a vyhněte se určitým upozorněním spojeným s úlohami .NET.

Syntaxe

async { expression }

Poznámky

V předchozí syntaxi je výpočet reprezentovaný expression tak, aby běžel asynchronně, tj. bez blokování aktuálního výpočtu vlákna při asynchronních operacích spánku, vstupně-výstupních operací a dalších asynchronních operacích. Asynchronní výpočty se často spouští ve vlákně na pozadí, zatímco provádění pokračuje v aktuálním vlákně. Typ výrazu je Async<'T>, kde 'T je typ vrácený výrazem při použití klíčového return slova.

Třída Async poskytuje metody, které podporují několik scénářů. Obecný přístup spočívá v vytváření Async objektů, které představují výpočty nebo výpočty, které chcete spustit asynchronně, a pak tyto výpočty spustit pomocí jedné z aktivačních funkcí. Aktivace, kterou použijete, závisí na tom, jestli chcete použít aktuální vlákno, vlákno na pozadí nebo objekt úlohy .NET. Chcete-li například spustit asynchronní výpočty v aktuálním vlákně, můžete použít Async.StartImmediate. Když spustíte asynchronní výpočet z vlákna uživatelského rozhraní, nezablokujete hlavní smyčku událostí, která zpracovává akce uživatele, jako jsou stisknutí kláves a aktivita myši, takže vaše aplikace zůstane responzivní.

Asynchronní vazba pomocí let!

V asynchronním výrazu jsou některé výrazy a operace synchronní a některé jsou asynchronní. Při asynchronním volání metody namísto obyčejné let vazby použijete let!. Výsledkem let! je umožnit provádění pokračovat v jiných výpočtech nebo vláknech při provádění výpočtů. Jakmile se vrátí pravá strana let! vazby, zbytek asynchronního výrazu obnoví provádění.

Následující kód ukazuje rozdíl mezi let a let!. Řádek kódu, který používá let pouze vytvoří asynchronní výpočet jako objekt, který můžete spustit později pomocí, například Async.StartImmediate nebo Async.RunSynchronously. Řádek kódu, který používá let! spuštění výpočtu a provádí asynchronní čekání: vlákno je pozastaveno, dokud nebude k dispozici výsledek, v jakém okamžiku bude provádění pokračovat.

// 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! lze použít pouze k přímému čekání asynchronních výpočtů jazyka Async<T> F#. Můžete očekávat další druhy asynchronních operací nepřímo:

  • Úlohy .NET a negenerické Taskúlohy Task<TResult> pomocí kombinaceAsync.AwaitTask
  • Úlohy hodnot .NET a ne generické ValueTask, ValueTask<TResult> kombinováním a .AsTask()Async.AwaitTask
  • Libovolný objekt, který následuje za vzorem GetAwaiter zadaným v F# RFC FS-1097, zkombinováním s task { return! expr } |> Async.AwaitTask.

Tok řízení

Asynchronní výrazy mohou zahrnovat konstrukty toku řízení, jako for .. in .. dojsou , while .. do, try .. with .., try .. finally .., , if .. then .. elsea if .. then ... Ty pak mohou zahrnovat další asynchronní konstrukce s výjimkou with obslužných finally rutin, které se provádějí synchronně.

Asynchronní výrazy jazyka F# nepodporují asynchronní try .. finally ..výrazy . V tomto případě můžete použít výraz úkolu.

use a use! vazby

V rámci asynchronních výrazů mohou vazby use svázat s hodnotami typu IDisposable. V případě druhé operace čištění odstranění se provádí asynchronně.

Kromě let!toho můžete provádět use! asynchronní vazby. Rozdíl mezi let! a use! je stejný jako rozdíl mezi let a use. Pro use!, objekt je uvolněn na konci aktuálního oboru. Všimněte si, use! že v aktuální verzi jazyka F# neumožňuje inicializaci hodnoty na hodnotu null, i když use ano.

Asynchronní primitivy

Metoda, která provádí jednu asynchronní úlohu a vrací výsledek, se nazývá asynchronní primitiva, a jsou navrženy speciálně pro použití s let!. V základní knihovně jazyka F# je definováno několik asynchronních primitiv. Dvě takové metody pro webové aplikace jsou definovány v modulu FSharp.Control.WebExtensions: WebRequest.AsyncGetResponse a WebClient.AsyncDownloadString. Obě primitiva stahují data z webové stránky s adresou URL. AsyncGetResponseSystem.Net.WebResponse vytvoří objekt a AsyncDownloadString vytvoří řetězec, který představuje html pro webovou stránku.

Modul obsahuje FSharp.Control.CommonExtensions několik primitiv pro asynchronní vstupně-výstupní operace. Tyto rozšiřující metody System.IO.Stream třídy jsou Stream.AsyncRead a Stream.AsyncWrite.

Můžete také napsat vlastní asynchronní primitivy definováním funkce nebo metody, jejíž tělo je asynchronní výraz.

Pokud chcete použít asynchronní metody v rozhraní .NET Framework, které jsou navržené pro jiné asynchronní modely s asynchronním programovacím modelem jazyka F#, vytvoříte funkci, která vrátí objekt F# Async . Knihovna jazyka F# má funkce, které usnadňují práci.

Tady je uveden jeden příklad použití asynchronních výrazů; v dokumentaci k metodám třídy Async existuje mnoho dalších.

Tento příklad ukazuje, jak paralelně spouštět kód pomocí asynchronních výrazů.

V následujícím příkladu kódu získá funkce fetchAsync text HTML vrácený z webového požadavku. Funkce fetchAsync obsahuje asynchronní blok kódu. Při vytvoření vazby na výsledek asynchronní primitiv, v tomto případě AsyncDownloadStringse let! použije místo let.

Pomocí funkce Async.RunSynchronously spustíte asynchronní operaci a počkáte na její výsledek. Jako příklad můžete paralelně spouštět více asynchronních operací pomocí Async.Parallel funkce společně s Async.RunSynchronously funkcí. Funkce Async.Parallel vezme seznam Async objektů, nastaví kód pro každý Async objekt úkolu, který se spustí paralelně, a vrátí Async objekt, který představuje paralelní výpočty. Stejně jako u jedné operace voláte Async.RunSynchronously , aby se spustilo spuštění.

Funkce runAll spustí paralelně tři asynchronní výrazy a počká, dokud se nedokončí.

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()

Viz také