Ekspresi asinkron

Artikel ini menjelaskan dukungan dalam F# untuk ekspresi asinkron. Ekspresi asinkron menyediakan satu cara untuk melakukan komputasi secara asinkron, yaitu tanpa menghalangi 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 beroperasi secara ekstensif dengan pustaka .NET yang membuat atau menggunakan tugas .NET. Saat menulis sebagian besar kode asinkron dalam F#, ekspresi asinkron F# lebih disukai karena lebih ringkas, lebih komposisional, dan menghindari peringatan tertentu terkait dengan tugas .NET.

Sintaks

async { expression }

Keterangan

Dalam sintaks sebelumnya, komputasi yang diwakili oleh expression diatur untuk berjalan secara asinkron, yaitu, tanpa memblokir utas komputasi saat ini ketika operasi tidur asinkron, I/O, dan operasi asinkron lainnya dilakukan. Komputasi asinkron sering dimulai pada utas latar belakang sementara eksekusi berlanjut di utas saat ini. Jenis ekspresinya adalah Async<'T>, di mana 'T adalah jenis yang dikembalikan oleh ekspresi saat kata kunci return digunakan.

Kelas Async ini menyediakan metode yang mendukung beberapa skenario. Pendekatan umumnya adalah membuat objek Async 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 let biasa, Anda menggunakan let!. Efek dari let! adalah mengaktifkan eksekusi untuk melanjutkan komputasi atau utas lain saat komputasi sedang dilakukan. Setelah sisi kanan pengikatan let! 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 asinkron F# Async<T> secara langsung. Anda dapat menunggu jenis operasi asinkron lainnya secara tidak langsung:

  • Tugas .NET, Task<TResult> dan Task non-generik, dengan menggabungkan Async.AwaitTask
  • Tugas nilai .NET, ValueTask<TResult> dan ValueTask non-generik, dengan menggabungkan .AsTask() dan Async.AwaitTask
  • Objek apa pun yang mengikuti pola "GetAwaiter" yang ditentukan dalam F# RFC FS-1097, dengan menggabungkan 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 mungkin pada gilirannya mencakup konstruksi asinkron lebih lanjut, dengan pengecualian penanganan with dan finally, yang dijalankan secara sinkron.

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

pengikatan use danuse!

Dalam ekspresi asinkron, pengikatan use dapat mengikat ke nilai jenis IDisposable. Untuk yang terakhir, operasi pembersihan pembuangan dijalankan 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 mengizinkan nilai diinisialisasi ke null, meskipun use demikian.

Primitif Asinkron

Metode yang melakukan tugas asinkron tunggal 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 WebClient.AsyncDownloadString. Kedua primitif mengunduh data dari halaman Web, diberikan URL. AsyncGetResponse menghasilkan objek System.Net.WebResponse, dan AsyncDownloadString menghasilkan string yang merepresentasikan HTML untuk halaman Web.

Beberapa primitif untuk operasi I/O asinkron disertakan dalam modul FSharp.Control.CommonExtensions. Metode ekstensi kelas System.IO.Stream 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 lain dengan model pemrograman asinkron F#, Anda membuat fungsi yang mengembalikan objek Async F#. Pustaka F# memiliki fungsi yang membuatnya mudah untuk dilakukan.

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

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 fetchAsync ini berisi blok kode asinkron. Ketika pengikatan dilakukan pada hasil primitif asinkron, dalam hal ini AsyncDownloadString, 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 fungsi Async.Parallel bersama dengan fungsi Async.RunSynchronously. Fungsi Async.Parallel ini mengambil daftar objek Async, menyiapkan kode untuk setiap objek tugas Async agar berjalan secara paralel, dan mengembalikan objek Async yang mewakili komputasi paralel. Sama seperti untuk satu operasi, Anda memanggil Async.RunSynchronously untuk memulai eksekusi.

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

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

Lihat juga