Bagikan melalui


Ekspresi asinkron

Artikel ini menjelaskan dukungan di F# untuk ekspresi asinkron. Ekspresi asinkron menyediakan salah satu cara melakukan komputasi secara asinkron, yaitu, tanpa memblokir eksekusi pekerjaan lain. Misalnya, komputasi asinkron dapat digunakan untuk menulis aplikasi yang memiliki UI yang tetap responsif terhadap pengguna saat aplikasi melakukan pekerjaan lain. Model pemrograman Alur Kerja Asinkron F# memungkinkan Anda menulis program fungsional sambil menyembunyikan detail transisi alur dalam pustaka.

Kode asinkron juga dapat ditulis menggunakan ekspresi tugas, yang membuat tugas .NET secara langsung. Menggunakan ekspresi tugas lebih disukai saat mengoperasikan secara ekstensif dengan pustaka .NET yang membuat atau menggunakan tugas .NET. Saat menulis kode yang paling asinkron dalam F#, ekspresi asinkron F# lebih disukai karena lebih ringkas, lebih banyak komposisi, dan menghindari peringatan tertentu yang terkait dengan tugas .NET.

Sintaksis

async { expression }

Komentar

Dalam sintaks sebelumnya, komputasi yang diwakili oleh expression disiapkan untuk berjalan secara asinkron, yaitu, tanpa memblokir utas komputasi saat ini ketika operasi tidur asinkron, I/O, dan operasi asinkron lainnya dilakukan. Jenis ekspresi adalah Async<'T>, di mana 'T adalah jenis yang dikembalikan oleh ekspresi saat return kata kunci digunakan.

Kelas ini Async menyediakan metode yang mendukung beberapa skenario. Pendekatan umumnya adalah membuat Async objek yang mewakili komputasi atau komputasi yang ingin Anda jalankan secara asinkron, lalu memulai komputasi ini dengan menggunakan salah satu fungsi pemicu. Pemicu yang Anda gunakan tergantung pada apakah Anda ingin menggunakan utas saat ini, utas latar belakang, atau objek tugas .NET. Misalnya, untuk memulai komputasi asinkron pada utas saat ini, Anda dapat menggunakan Async.StartImmediate. Saat Anda memulai komputasi asinkron dari utas UI, Anda tidak memblokir perulangan peristiwa utama yang memproses tindakan pengguna seperti penekanan tombol dan aktivitas mouse, sehingga aplikasi Anda tetap responsif.

Pengikatan Asinkron dengan Menggunakan let!

Dalam ekspresi asinkron, beberapa ekspresi dan operasi sinkron, dan beberapa asinkron. Ketika Anda memanggil metode secara asinkron, alih-alih pengikatan biasa let , Anda menggunakan let!. Efek dari let! adalah memungkinkan eksekusi untuk berlanjut pada komputasi atau utas lain saat komputasi sedang dilakukan. Setelah sisi let! kanan pengikatan kembali, ekspresi asinkron lainnya melanjutkan eksekusi.

Kode berikut menunjukkan perbedaan antara let dan let!. Baris kode yang menggunakan let hanya membuat komputasi asinkron sebagai objek yang dapat Anda jalankan nanti dengan menggunakan, misalnya, Async.StartImmediate atau Async.RunSynchronously. Baris kode yang menggunakan let! memulai komputasi dan melakukan penantian asinkron: utas ditangguhkan hingga hasilnya tersedia, di mana eksekusi titik berlanjut.

// 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! hanya dapat digunakan untuk menunggu komputasi Async<T> asinkron F# secara langsung. Anda dapat menunggu jenis operasi asinkron lainnya secara tidak langsung:

  • .NET tasks, Task<TResult> dan non-generic Task, dengan menggabungkan dengan Async.AwaitTask
  • Tugas nilai .NET, ValueTask<TResult> dan non-generic ValueTask, dengan menggabungkan dengan .AsTask() dan Async.AwaitTask
  • Objek apa pun yang mengikuti pola "GetAwaiter" yang ditentukan dalam F# RFC FS-1097, dengan menggabungkan dengan task { return! expr } |> Async.AwaitTask.

Alur Kontrol

Ekspresi asinkron dapat mencakup konstruksi alur kontrol, seperti for .. in .. do, , while .. do, try .. with ..try .. finally .., if .. then .. else, dan if .. then ... Ini dapat, pada gilirannya, mencakup konstruksi asinkron lebih lanjut, dengan pengecualian penangan with dan finally , yang dijalankan secara sinkron.

Ekspresi asinkron F# tidak mendukung asinkron try .. finally ... Anda dapat menggunakan ekspresi tugas untuk kasus ini.

use dan use! pengikatan

Dalam ekspresi asinkron, use pengikatan dapat mengikat ke nilai jenis IDisposable. Untuk yang disebutkan terakhir, operasi pembersihan limbah dilakukan secara asinkron.

Selain let!, Anda dapat menggunakan use! untuk melakukan pengikatan asinkron. Perbedaan antara let! dan use! sama dengan perbedaan antara let dan use. Untuk use!, objek dibuang pada penutupan cakupan saat ini. Perhatikan bahwa dalam rilis F#saat ini, use! tidak memungkinkan nilai untuk diinisialisasi menjadi null, meskipun use tidak.

Primitif Asinkron

Metode yang melakukan satu tugas asinkron dan mengembalikan hasilnya disebut primitif asinkron, dan ini dirancang khusus untuk digunakan dengan let!. Beberapa primitif asinkron didefinisikan dalam pustaka inti F#. Dua metode tersebut untuk aplikasi Web didefinisikan dalam modul FSharp.Control.WebExtensions: WebRequest.AsyncGetResponse dan HttpClient.GetStringAsync (dibungkus dengan Async.AwaitTask untuk kompatibilitas dengan model asinkron F#). Kedua primitif mengunduh data dari halaman Web, diberikan URL. AsyncGetResponse System.Net.WebResponse menghasilkan objek, dan GetStringAsync menghasilkan string yang mewakili HTML untuk halaman Web.

Beberapa primitif untuk operasi I/O asinkron disertakan dalam FSharp.Control.CommonExtensions modul. Metode System.IO.Stream ekstensi kelas ini adalah Stream.AsyncRead dan Stream.AsyncWrite.

Anda juga dapat menulis primitif asinkron Anda sendiri dengan mendefinisikan fungsi atau metode yang isinya adalah ekspresi asinkron.

Untuk menggunakan metode asinkron dalam .NET Framework yang dirancang untuk model asinkron lainnya dengan model pemrograman asinkron F#, Anda membuat fungsi yang mengembalikan objek F# Async . Pustaka F# memiliki fungsi yang membuatnya mudah dilakukan.

Salah satu contoh penggunaan ekspresi asinkron disertakan di sini; ada banyak orang lain dalam dokumentasi untuk metode kelas Asinkron.

Contoh ini menunjukkan cara menggunakan ekspresi asinkron untuk menjalankan kode secara paralel.

Dalam contoh kode berikut, fungsi fetchAsync mendapatkan teks HTML yang dikembalikan dari permintaan Web. Fungsi ini fetchAsync berisi blok kode asinkron. Ketika pengikatan dilakukan pada hasil primitif asinkron, dalam hal AsyncDownloadStringini , let! digunakan alih-alih let.

Anda menggunakan fungsi Async.RunSynchronously untuk menjalankan operasi asinkron dan menunggu hasilnya. Sebagai contoh, Anda dapat menjalankan beberapa operasi asinkron secara paralel dengan menggunakan Async.Parallel fungsi bersama dengan Async.RunSynchronously fungsi . Fungsi ini Async.Parallel mengambil daftar Async objek, menyiapkan kode untuk setiap Async objek tugas yang akan dijalankan secara paralel, dan mengembalikan Async objek yang mewakili komputasi paralel. Sama seperti untuk satu operasi, Anda memanggil Async.RunSynchronously untuk memulai eksekusi.

Fungsi ini runAll meluncurkan tiga ekspresi asinkron secara paralel dan menunggu sampai semuanya selesai.

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

Lihat juga