Pemrograman Asinkron: masa depan
Isi
Yang penting:
- Kode di Dart berjalan dalam satu utas ( catatan utas - utas ) eksekusi.
- Karena kode yang memakan waktu (blok) utas untuk waktu yang lama, program mungkin membeku.
- Objek
Future
( futures
) mewakili hasil operasi asinkron - pemrosesan atau I / O, yang akan diselesaikan kemudian. - Untuk menunda eksekusi hingga selesai di masa mendatang, gunakan
await
di fungsi asinkron (atau then()
saat menggunakan API Future
). - Untuk menangkap kesalahan, gunakan konstruksi
try-catch
(atau catchError()
saat menggunakan API Future
) dalam fungsi asinkron. - Untuk pemrosesan simultan, buat isolat (atau pekerja untuk aplikasi web).
Kode di Dart berjalan dalam utas eksekusi tunggal. Jika kode sibuk dengan perhitungan panjang atau sedang menunggu operasi I / O, maka seluruh program dijeda.
Operasi asinkron memungkinkan program Anda untuk menyelesaikan tugas-tugas lain sambil menunggu operasi selesai. Dart menggunakan futures
untuk menyajikan hasil operasi asinkron. Anda juga dapat menggunakan async dan menunggu atau Future API untuk bekerja dengan futures
.
Sebuah catatan
Semua kode dieksekusi dalam konteks isolat, yang memiliki semua memori yang digunakan oleh kode. Lebih dari satu eksekusi kode tidak dapat dimulai pada isolat yang sama.
Untuk eksekusi paralel blok kode, Anda dapat memisahkannya menjadi isolat terpisah. (Aplikasi Web menggunakan pekerja alih-alih isolat.) Biasanya, masing-masing isolat berjalan pada inti prosesornya sendiri. Isolat tidak berbagi memori, dan satu-satunya cara mereka berinteraksi adalah saling mengirim pesan. Untuk menyelami topik ini, lihat dokumentasi untuk isolat atau pekerja .
Pendahuluan
Mari kita lihat contoh kode yang dapat "membekukan" eksekusi program:
Program kami membaca berita dari file untuk hari itu, menampilkannya, lalu menampilkan informasi yang masih menarik bagi pengguna:
<gathered news goes here> Winning lotto numbers: [23, 63, 87, 26, 2] Tomorrow's forecast: 70F, sunny. Baseball score: Red Sox 10, Yankees 0
Dalam contoh ini, masalahnya adalah bahwa semua operasi setelah memanggil gatherNewsReports()
akan menunggu sampai gatherNewsReports()
akan mengembalikan konten file, tidak peduli berapa lama. Jika membaca file membutuhkan waktu lama, pengguna akan dipaksa untuk menunggu hasil undian, ramalan cuaca dan pemenang dari permainan baru-baru ini.
Untuk mempertahankan respons aplikasi, penulis Dart menggunakan model asinkron untuk mengidentifikasi fungsi yang berpotensi melakukan pekerjaan mahal. Fungsi tersebut mengembalikan nilainya menggunakan futures
.
Apa masa depan
future
- turunan dari kelas Future <T> , yang merupakan operasi asinkron yang mengembalikan hasil tipe T. Jika hasil operasi tidak digunakan, maka tipe future
ditunjukkan oleh Future<void>
. Saat memanggil fungsi yang mengembalikan future
, dua hal terjadi:
- Fungsi antrian untuk eksekusi dan mengembalikan objek
Future
tidak lengkap. - Kemudian, ketika operasi selesai,
future
berakhir dengan nilai atau kesalahan.
Untuk menulis kode yang tergantung di masa future
, Anda memiliki dua opsi:
- Gunakan
async
- await
- Gunakan
Future
API
Async - tunggu
async
dan await
kata kunci adalah bagian dari dukungan async
Dart. Mereka memungkinkan Anda untuk menulis kode asinkron yang terlihat seperti kode sinkron dan tidak menggunakan API Future
. Fungsi asinkron adalah fungsi dengan kata kunci async
di depan tubuhnya. Kata kunci yang await
hanya berfungsi dalam fungsi asinkron.
Catatan: di Dart 1.x, fungsi asinkron segera menunda eksekusi. Di Dart 2, alih-alih segera berhenti, fungsi asinkron menjalankan secara sinkron sampai yang pertama await
atau return
.
Kode berikut mensimulasikan membaca berita dari file menggunakan async
- await
. Buka DartPad dengan aplikasi , luncurkan dan klik CONSOLE untuk melihat hasilnya.
Perhatikan bahwa kita pertama-tama memanggil printDailyNewsDigest()
, tetapi berita dicetak terakhir, bahkan jika file tersebut hanya berisi satu baris. Ini karena kode yang membaca dan mencetak file berjalan secara tidak sinkron.
Dalam contoh ini, printDailyNewsDigest()
melakukan panggilan ke gatherNewsReports()
, yang non-pemblokiran. Memanggil metode gatherNewsReports()
pekerjaan, tetapi tidak menghentikan sisa kode dari mengeksekusi. Program ini menampilkan angka lotre, perkiraan dan skor pertandingan bisbol; Program ini mencetak berita setelah pengumpulan gatherNewsReports()
. Jika gatherNewsReports()
membutuhkan waktu untuk menyelesaikan pekerjaannya, tidak ada hal buruk terjadi: pengguna dapat membaca hal-hal lain sebelum ringkasan berita harian dicetak.
Perhatikan jenis yang dikembalikan. Tipe gatherNewsReports()
fungsi gatherNewsReports()
adalah Future<String>
, yang berarti bahwa ia mengembalikan future
yang berakhir dengan nilai string. Fungsi printDailyNewsDigest()
, yang tidak mengembalikan nilai, memiliki tipe pengembalian Future<void>
.
Diagram berikut menunjukkan langkah-langkah pelaksanaan kode.

- Aplikasi mulai berjalan.
- Fungsi
main()
printDailyNewsDigest()
fungsi asinkron printDailyNewsDigest()
, yang mulai berjalan secara sinkron. printDailyNewsDigest()
menggunakan await
untuk memanggil fungsi gatherNewsReports()
, yang mulai berjalan.gatherNewsReports()
mengembalikan future
belum selesai (turunan Future<String>
).- Karena
printDailyNewsDigest()
adalah fungsi asinkron dan mengharapkan suatu nilai, itu menjeda eksekusi dan mengembalikan future
tidak lengkap (dalam hal ini, Future<void>
) ke fungsi main ()
. - Fungsi output lainnya dijalankan. Karena mereka sinkron, setiap fungsi dilakukan sepenuhnya sebelum pindah ke yang berikutnya. Misalnya, semua nomor lotere yang menang akan ditampilkan sebelum ramalan cuaca.
- Setelah menyelesaikan
main()
fungsi asinkron dapat melanjutkan eksekusi. Pertama, kita mendapatkan future
dengan berita tentang selesainya gatherNewsReports()
. Kemudian printDailyNewsDigest()
melanjutkan eksekusi, menampilkan berita. - Pada akhir eksekusi
printDailyNewsDigest()
, future
diterima awalnya future
dan aplikasi keluar.
Perhatikan bahwa fungsi asinkron dimulai segera (secara sinkron). Fungsi ini menjeda eksekusi dan mengembalikan future
belum selesai saat kemunculan pertama dari salah satu dari berikut ini terjadi:
- Ekspresi
await
pertama (setelah fungsi mendapatkan future
tidak lengkap dari ekspresi ini). return
dalam suatu fungsi.- Akhir dari fungsi tubuh.
Menangani kesalahan
Kemungkinan besar Anda ingin "menangkap" kesalahan dalam pelaksanaan fungsi yang mengembalikan future
. Dalam fungsi asinkron, Anda dapat menangani kesalahan menggunakan try-catch
:
Future<void> printDailyNewsDigest() async { try { var newsDigest = await gatherNewsReports(); print(newsDigest); } catch (e) {
try-catch
dengan kode asynchronous berperilaku sama dengan kode sinkron: jika kode di blok try
pengecualian, kode di dalam catch
dijalankan.
Eksekusi berurutan
Anda dapat menggunakan beberapa ekspresi await
untuk memastikan bahwa setiap pernyataan selesai sebelum menjalankan yang berikut:
Fungsi expensiveB()
tidak dieksekusi sampai expensiveA()
selesai, dan seterusnya.
API masa depan
Sebelum async
dan await
ditambahkan di Dart 1.9, Anda harus menggunakan API Future
. Anda masih dapat melihat penggunaan Future
API dalam kode lama dan dalam kode yang membutuhkan lebih banyak fungsi daripada yang ditawarkan asyncโawait
.
Untuk menulis kode asinkron menggunakan API Future
, gunakan metode then()
untuk mendaftarkan panggilan balik. Panggilan balik ini akan berfungsi saat future
selesai.
Kode berikut mensimulasikan pembacaan berita dari file menggunakan Future
API. Buka DartPad dengan aplikasi , luncurkan dan klik CONSOLE untuk melihat hasilnya.
Perhatikan bahwa kita pertama-tama memanggil printDailyNewsDigest()
, tetapi berita dicetak terakhir, bahkan jika file tersebut hanya berisi satu baris. Ini karena kode yang membaca dan mencetak file berjalan secara tidak sinkron.
Aplikasi ini berjalan sebagai berikut:
- Aplikasi mulai berjalan.
- Fungsi utama memanggil
printDailyNewsDigest()
, yang tidak langsung mengembalikan hasilnya, tetapi panggilan pertama gatherNewsReports()
. gatherNewsReports()
mulai membaca berita dan kembali di future
.printDailyNewsDigest()
menggunakan then()
untuk mendaftarkan panggilan balik yang akan mengambil sebagai parameter nilai yang diperoleh pada akhir future
. Panggilan then()
mengembalikan future
baru, yang berakhir dengan nilai yang dikembalikan oleh callback dari then()
.- Sisa dari fungsi output dieksekusi. Karena mereka sinkron, setiap fungsi dilakukan sepenuhnya sebelum pindah ke yang berikutnya. Misalnya, semua nomor lotere yang menang akan ditampilkan sebelum ramalan cuaca.
- Ketika semua berita telah diterima,
future
dikembalikan oleh fungsi gatherNewsReports()
berakhir dengan string yang berisi berita yang dikumpulkan. - Kode yang ditentukan
then()
di printDailyNewsDigest()
dijalankan untuk printDailyNewsDigest()
berita. - Aplikasi dimatikan.
Catatan: dalam fungsi printDailyNewsDigest()
, kode future.then(print)
setara dengan yang berikut: future.then((newsDigest) => print(newsDigest))
.
Selain itu, kode di dalamnya then()
dapat menggunakan kurung kurawal:
Future<void> printDailyNewsDigest() { final future = gatherNewsReports(); return future.then((newsDigest) { print(newsDigest);
Anda harus menentukan argumen panggilan balik di then()
, bahkan jika future
adalah tipe Future<void>
. Dengan konvensi, argumen yang tidak digunakan didefinisikan melalui _
(garis bawah).
final future = printDailyNewsDigest(); return future.then((_) {
Menangani kesalahan
Menggunakan Future
API, Anda dapat menangkap kesalahan menggunakan catchError()
:
Future<void> printDailyNewsDigest() => gatherNewsReports().then(print).catchError(handleError);
Jika berita tidak dapat dibaca, maka kode di atas dijalankan sebagai berikut:
future
dikembalikan oleh gatherNewsReports()
gagal.future
dikembalikan then()
gagal, print()
tidak dipanggil.catchError()
di catchError()
( handleError()
) menangkap kesalahan, future
dikembalikan oleh catchError()
selesai secara normal, dan kesalahan tidak menyebar lebih lanjut.
Rantai catchError()
- catchError()
adalah pola umum saat menggunakan API Future
. Pertimbangkan pasangan ini sebagai setara dengan try-catch
di Future
API.
Seperti then (), catchError () mengembalikan future
baru yang berakhir dengan nilai balik dari callback. Untuk menyelami topik, baca Futures and Error Handling .
Memanggil beberapa fungsi untuk mengembalikan future
Mari kita perhatikan tiga fungsi: expensiveA()
, expensiveB()
, expensiveC()
, yang mengembalikan future
. Anda dapat memanggil mereka secara berurutan (satu fungsi dimulai setelah yang sebelumnya selesai), atau Anda dapat menjalankan semuanya sekaligus dan melakukan sesuatu segera setelah semua nilai kembali. Antarmuka Future cukup fleksibel untuk mengimplementasikan kedua use case.
Rantai panggilan fungsi menggunakan then()
Ketika fungsi mengembalikan future
harus dijalankan secara berurutan, gunakan rantai sejak then()
:
expensiveA() .then((aValue) => expensiveB()) .then((bValue) => expensiveC()) .then((cValue) => doSomethingWith(cValue));
Melampirkan panggilan balik juga berfungsi, tetapi lebih sulit untuk dibaca. ( perhatikan http://callbackhell.com/ )
Menunggu beberapa futures
selesai menggunakan Future.wait()
Jika urutan eksekusi fungsi tidak penting, Anda dapat menggunakan Future.wait()
. Ketika Anda menentukan daftar futures
untuk parameter untuk fungsi Future.wait (), itu segera mengembalikan future
. future
ini tidak akan berakhir sampai semua futures
ditentukan futures
. future
ini akan berakhir dengan daftar hasil dari semua masa futures
ditunjukkan.
Future.wait([expensiveA(), expensiveB(), expensiveC()]) .then((List responses) => chooseBestResponse(responses, moreInfo)) .catchError(handleError);
Jika panggilan ke salah satu fungsi gagal, maka future
dikembalikan oleh Future.wait()
juga gagal. Gunakan catchError()
untuk menangkap kesalahan ini.
Apa lagi yang harus dibaca?
Dart 2. Pemrograman Asinkron: Aliran Data