Dart 2. Pemrograman asinkron: masa depan

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:


 // Synchronous code void printDailyNewsDigest() { var newsDigest = gatherNewsReports(); // Can take a while. print(newsDigest); } main() { printDailyNewsDigest(); printWinningLotteryNumbers(); printWeatherForecast(); printBaseballScore(); } 

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:


  1. Fungsi antrian untuk eksekusi dan mengembalikan objek Future tidak lengkap.
  2. 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.


Kode contoh
 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. import 'dart:async'; Future<void> printDailyNewsDigest() async { var newsDigest = await gatherNewsReports(); print(newsDigest); } main() { printDailyNewsDigest(); printWinningLotteryNumbers(); printWeatherForecast(); printBaseballScore(); } printWinningLotteryNumbers() { print('Winning lotto numbers: [23, 63, 87, 26, 2]'); } printWeatherForecast() { print("Tomorrow's forecast: 70F, sunny."); } printBaseballScore() { print('Baseball score: Red Sox 10, Yankees 0'); } const news = '<gathered news goes here>'; const oneSecond = Duration(seconds: 1); // Imagine that this function is more complex and slow. :) Future<String> gatherNewsReports() => Future.delayed(oneSecond, () => news); // Alternatively, you can get news from a server using features // from either dart:io or dart:html. For example: // // import 'dart:html'; // // Future<String> gatherNewsReportsFromServer() => HttpRequest.getString( // 'https://www.dartlang.org/f/dailyNewsDigest.txt', // ); 

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.



  1. Aplikasi mulai berjalan.
  2. Fungsi main() printDailyNewsDigest() fungsi asinkron printDailyNewsDigest() , yang mulai berjalan secara sinkron.
  3. printDailyNewsDigest() menggunakan await untuk memanggil fungsi gatherNewsReports() , yang mulai berjalan.
  4. gatherNewsReports() mengembalikan future belum selesai (turunan Future<String> ).
  5. 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 () .
  6. 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.
  7. Setelah menyelesaikan main() fungsi asinkron dapat melanjutkan eksekusi. Pertama, kita mendapatkan future dengan berita tentang selesainya gatherNewsReports() . Kemudian printDailyNewsDigest() melanjutkan eksekusi, menampilkan berita.
  8. 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) { // Handle error... } } 

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:


 // Sequential processing using async and await. main() async { await expensiveA(); await expensiveB(); doSomethingWith(await expensiveC()); } 

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.


Kode contoh
 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. import 'dart:async'; Future<void> printDailyNewsDigest() { final future = gatherNewsReports(); return future.then(print); // You don't *have* to return the future here. // But if you don't, callers can't await it. } main() { printDailyNewsDigest(); printWinningLotteryNumbers(); printWeatherForecast(); printBaseballScore(); } printWinningLotteryNumbers() { print('Winning lotto numbers: [23, 63, 87, 26, 2]'); } printWeatherForecast() { print("Tomorrow's forecast: 70F, sunny."); } printBaseballScore() { print('Baseball score: Red Sox 10, Yankees 0'); } const news = '<gathered news goes here>'; const oneSecond = Duration(seconds: 1); // Imagine that this function is more complex and slow. :) Future<String> gatherNewsReports() => Future.delayed(oneSecond, () => news); // Alternatively, you can get news from a server using features // from either dart:io or dart:html. For example: // // import 'dart:html'; // // Future<String> gatherNewsReportsFromServer() => HttpRequest.getString( // 'https://www.dartlang.org/f/dailyNewsDigest.txt', // ); 

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:


  1. Aplikasi mulai berjalan.
  2. Fungsi utama memanggil printDailyNewsDigest() , yang tidak langsung mengembalikan hasilnya, tetapi panggilan pertama gatherNewsReports() .
  3. gatherNewsReports() mulai membaca berita dan kembali di future .
  4. 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() .
  5. 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.
  6. Ketika semua berita telah diterima, future dikembalikan oleh fungsi gatherNewsReports() berakhir dengan string yang berisi berita yang dikumpulkan.
  7. Kode yang ditentukan then() di printDailyNewsDigest() dijalankan untuk printDailyNewsDigest() berita.
  8. 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); // Do something else... }); } 

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((_) { // Code that doesn't use the `_` parameter... print('All reports printed.'); }); 

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:


  1. future dikembalikan oleh gatherNewsReports() gagal.
  2. future dikembalikan then() gagal, print() tidak dipanggil.
  3. 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

Source: https://habr.com/ru/post/id442282/


All Articles