Dela via


Asynkrona uttryck

Den här artikeln beskriver stöd i F# för asynkrona uttryck. Asynkrona uttryck ger ett sätt att utföra beräkningar asynkront, det vill: utan att blockera körning av annat arbete. Till exempel kan asynkrona beräkningar användas för att skriva appar som har UIs som förblir dynamiska för användare när programmet utför annat arbete. Med programmeringsmodellen F# Asynkrona arbetsflöden kan du skriva funktionella program samtidigt som du döljer information om trådövergången i ett bibliotek.

Asynkron kod kan också redigeras med hjälp av uppgiftsuttryck som skapar .NET-uppgifter direkt. Användning av uppgiftsuttryck är att föredra när du samverkar i stor utsträckning med .NET-bibliotek som skapar eller använder .NET-uppgifter. När du skriver de flesta asynkrona kod i F# föredras Asynkrona F#-uttryck eftersom de är mer kortfattade, mer kompositionsbaserade och undviker vissa varningar som är associerade med .NET-uppgifter.

Syntax

async { expression }

Kommentarer

I den tidigare syntaxen är den beräkning som representeras av expression konfigurerad för att köras asynkront, det vill ex utan att blockera den aktuella beräkningstråden när asynkrona viloåtgärder, I/O och andra asynkrona åtgärder utförs. Asynkrona beräkningar startas ofta i en bakgrundstråd medan körningen fortsätter på den aktuella tråden. Uttryckets typ är Async<'T>, där 'T är den typ som returneras av uttrycket när nyckelordet return används.

Klassen Async innehåller metoder som stöder flera scenarier. Den allmänna metoden är att skapa Async objekt som representerar den beräkning eller de beräkningar som du vill köra asynkront och sedan starta dessa beräkningar med någon av de utlösande funktionerna. Vilken utlösare du använder beror på om du vill använda den aktuella tråden, en bakgrundstråd eller ett .NET-aktivitetsobjekt. Om du till exempel vill starta en asynkron beräkning på den aktuella tråden kan du använda Async.StartImmediate. När du startar en asynkron beräkning från användargränssnittstråden blockerar du inte huvudhändelseslingan som bearbetar användaråtgärder som tangenttryckningar och musaktivitet, så att programmet förblir responsivt.

Asynkron bindning med hjälp av let!

I ett asynkront uttryck är vissa uttryck och åtgärder synkrona och vissa är asynkrona. När du anropar en metod asynkront använder let!du i stället för en vanlig let bindning . Effekten av let! är att aktivera körning för att fortsätta på andra beräkningar eller trådar när beräkningen utförs. När den högra sidan av bindningen let! har returnerats återupptas körningen av resten av asynkrona uttrycket.

Följande kod visar skillnaden mellan let och let!. Kodraden som använder let skapar bara en asynkron beräkning som ett objekt som du kan köra senare med hjälp av till exempel Async.StartImmediate eller Async.RunSynchronously. Kodraden som använder let! startar beräkningen och utför en asynkron väntan: tråden pausas tills resultatet är tillgängligt, då körningen fortsätter.

// 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! kan endast användas för att invänta F#-asynkrona Async<T> beräkningar direkt. Du kan invänta andra typer av asynkrona åtgärder indirekt:

  • .NET-uppgifter Task<TResult> och icke-generiska Taskgenom att kombinera med Async.AwaitTask
  • .NET-värdeuppgifter ValueTask<TResult> och icke-generiska ValueTaskgenom att kombinera med .AsTask() och Async.AwaitTask
  • Alla objekt som följer "GetAwaiter"-mönstret som anges i F# RFC FS-1097, genom att kombinera med task { return! expr } |> Async.AwaitTask.

Kontrollflöde

Asynkrona uttryck kan innehålla konstruktioner för kontrollflöde, till exempel for .. in .. do, while .. do, try .. with .., try .. finally .., if .. then .. elseoch if .. then ... Dessa kan i sin tur omfatta ytterligare asynkrona konstruktioner, med undantag för with hanterare och finally som körs synkront.

Asynkrona F#-uttryck stöder inte asynkrona try .. finally ... Du kan använda ett uppgiftsuttryck för det här fallet.

use och use! bindningar

Inom asynkrona use uttryck kan bindningar binda till värden av typen IDisposable. För det senare körs rensningsåtgärden asynkront.

Förutom let!kan du använda use! för att utföra asynkrona bindningar. Skillnaden mellan let! och use! är densamma som skillnaden mellan let och use. För use!tas objektet bort i slutet av det aktuella omfånget. Observera att i den aktuella versionen av F#, use! tillåter inte att ett värde initieras till null, även om use det gör det.

Asynkrona primitiver

En metod som utför en enda asynkron uppgift och returnerar resultatet kallas för en asynkron primitiv, och de är särskilt utformade för användning med let!. Flera asynkrona primitiver definieras i F#-kärnbiblioteket. Två sådana metoder för webbprogram definieras i modulen FSharp.Control.WebExtensions: WebRequest.AsyncGetResponse och WebClient.AsyncDownloadString. Båda primitiverna laddar ned data från en webbsida, givet en URL. AsyncGetResponse skapar ett System.Net.WebResponse objekt och AsyncDownloadString skapar en sträng som representerar HTML-koden för en webbsida.

Flera primitiver för asynkrona I/O-åtgärder ingår i modulen FSharp.Control.CommonExtensions . Dessa tilläggsmetoder för System.IO.Stream klassen är Stream.AsyncRead och Stream.AsyncWrite.

Du kan också skriva egna asynkrona primitiver genom att definiera en funktion eller metod vars brödtext är ett asynkront uttryck.

Om du vill använda asynkrona metoder i .NET Framework som är utformade för andra asynkrona modeller med den asynkrona F#-programmeringsmodellen skapar du en funktion som returnerar ett F#- Async objekt. F#-biblioteket har funktioner som gör det enkelt att göra det.

Ett exempel på hur du använder asynkrona uttryck ingår här. det finns många andra i dokumentationen för metoderna i klassen Async.

Det här exemplet visar hur du använder asynkrona uttryck för att köra kod parallellt.

I följande kodexempel hämtar en funktion fetchAsync HTML-texten som returneras från en webbbegäran. Funktionen fetchAsync innehåller ett asynkront kodblock. När en bindning görs till resultatet av en asynkron primitiv används i det här fallet AsyncDownloadStringlet! i stället för let.

Du använder funktionen Async.RunSynchronously för att köra en asynkron åtgärd och vänta på resultatet. Du kan till exempel köra flera asynkrona åtgärder parallellt med hjälp Async.Parallel av funktionen tillsammans med Async.RunSynchronously funktionen. Funktionen Async.Parallel tar en lista över objekten Async , konfigurerar koden för varje Async aktivitetsobjekt som ska köras parallellt och returnerar ett Async objekt som representerar den parallella beräkningen. Precis som för en enda åtgärd anropar Async.RunSynchronously du för att starta körningen.

Funktionen runAll startar tre asynkrona uttryck parallellt och väntar tills alla har slutförts.

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

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 webClient = new WebClient()
            let! html = webClient.AsyncDownloadString(uri)
            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()

Se även