8.2. Coroutine dan Task¶
Coroutine adalah unit kerja yang menjadi dasar program asyncio; task adalah cara sebuah aplikasi menjalankan beberapa coroutine secara bersamaan.
8.2.1. Coroutine¶
Sebuah coroutine adalah fungsi yang dideklarasikan dengan async def
import asyncio
async def heartbeat(interval_ms):
while True:
print("tick")
await asyncio.sleep_ms(interval_ms)
Isinya terlihat seperti fungsi biasa, dengan satu bahan tambahan: await. Di mana pun coroutine harus menunggu sesuatu -- tidur, pembacaan jaringan, suatu event yang di-set -- ia akan awaits sebuah ekspresi yang tahu cara menangguhkan coroutine hingga hal yang ditunggu siap. Pada setiap await, coroutine menyerahkan kendali kembali ke asyncio; asyncio melanjutkannya dari titik yang sama setelah operasi yang ditunggu selesai.
Modul asyncio menyertakan dua fungsi sleep:
asyncio.sleep()-- argumen dalam detik, menerima float.asyncio.sleep_ms()-- argumen dalam milidetik, menerima int. Ini adalah ekstensi MicroPython; biasanya pilihan yang tepat di kamera karena pengaturan waktu di firmware berbasis milidetik.
Sebuah async def yang berdiri sendiri tidak melakukan apa-apa dengan sendirinya. Memanggil heartbeat(500) tidak menjalankan isinya; ia mengembalikan sebuah objek coroutine yang harus dijadwalkan oleh asyncio. Cara paling sederhana untuk menjadwalkan satu adalah asyncio.run()
asyncio.run(heartbeat(500))
asyncio.run() memulai event loop, menjadwalkan coroutine yang diberikan sebagai titik masuk tingkat atas, menjalankan loop hingga coroutine tersebut selesai, lalu menutup loop. Untuk satu coroutine itulah seluruh program. Untuk beberapa coroutine, aplikasi menggunakan task.
8.2.2. Task¶
Sebuah task adalah pembungkus asyncio di sekitar sebuah coroutine yang berarti jadwalkan ini secara bersamaan dengan yang sekarang dan biarkan aku terus berjalan. asyncio.create_task() membuat satu dan mengembalikan objek Task yang merepresentasikan pekerjaan yang dijadwalkan:
task = asyncio.create_task(heartbeat("fast", 100))
Coroutine sekarang ada dalam jadwal loop; pemanggil tidak menunggunya. Objek Task yang dikembalikan adalah handle yang digunakan pemanggil setelahnya untuk berinteraksi dengan pekerjaan yang sedang berjalan tersebut.
Setelah aplikasi memiliki handle, ia dapat melakukan tiga hal dengannya:
Menunggu task selesai. Sebuah
Tasksendiri dapat di-await.result = await taskmenangguhkan coroutine saat ini hingga coroutinetaskselesai, kemudian dilanjutkan dengan apa pun yang dikembalikan coroutine tersebut (atau memunculkan kembali apa pun yang di-raise-nya).Membatalkan task.
task.cancel()menjadwalkanasyncio.CancelledErroruntuk di-raise di dalam coroutine task padaawaitberikutnya, memberinya kesempatan menjalankan kode pembersihan di blokfinally. Halaman tentang timeout dan pembatalan membahas detailnya.Mengidentifikasinya nanti.
asyncio.current_task()mengembalikanTaskuntuk coroutine yang sedang berjalan saat ini. Sebagian besar skrip tidak pernah memanggilnya; ia muncul dalam instrumentasi dan handler eksepsi.
Skrip tidak harus menangkap handle setiap saat. Task latar belakang sekali pakai yang dimulai aplikasi dan dibiarkan berjalan dapat membuang nilai kembaliannya -- loop tetap menjadwalkannya:
import asyncio
async def heartbeat(name, interval_ms):
while True:
print(name)
await asyncio.sleep_ms(interval_ms)
async def main():
asyncio.create_task(heartbeat("fast", 100))
asyncio.create_task(heartbeat("slow", 500))
await asyncio.sleep(5)
asyncio.run(main())
Dua panggilan create_task menjadwalkan kedua heartbeat tanpa menunggu salah satunya. Kendali segera kembali ke main, yang kemudian awaits tidur selama lima detik. Sementara ia tidur, kedua task heartbeat membuat kemajuan; loop bersiklus melalui task mana pun yang siap berjalan. Setelah lima detik main selesai, loop menutup task yang masih aktif, dan asyncio.run() kembali ke pemanggil.
Tangkap handle kapan pun aplikasi benar-benar membutuhkan salah satu dari tiga operasi di atas. Dalam praktiknya itu berarti hampir selalu, karena menutup aplikasi dengan bersih berarti membatalkan task latar belakang yang dimunculkannya -- halaman pembatalan membahas polanya.
8.2.3. Aturan dua baris¶
Program asyncio minimal adalah dua baris yang mengakhiri contoh-contoh di atas:
async def main():
...
asyncio.run(main())
Semua hal lainnya -- task yang dibuat aplikasi, primitif yang digunakan untuk mengkoordinasikannya, stream yang dibukanya -- terjadi di dalam main (dan di dalam coroutine yang dimunculkan main). Ketika sebuah skrip sudah melampaui loop klasik while True: csi0.snapshot() kamera, jawabannya bukan memanggil asyncio.run() di beberapa tempat; melainkan melipat pekerjaan baru ke dalam main sebagai task tambahan.