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í kombinace
Async.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 .. do
jsou , while .. do
, try .. with ..
, try .. finally ..
, , if .. then .. else
a 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. AsyncGetResponse
System.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ě AsyncDownloadString
se 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()