Kita telah memakai async def, await, dan asyncio.run() sebagai satu paket. Sekarang saatnya membongkar isi paket itu: apa sebenarnya coroutine object, siapa yang menjalankannya, dan mengapa seluruh program async Python berporos pada satu penjadwal bernama event loop. Memahami tiga bagian ini membuat pesan error asyncio yang semula terasa misterius menjadi masuk akal.
Coroutine adalah Object yang Menunggu Digerakkan #
Coroutine bukan fungsi ajaib yang berjalan sendiri. Coroutine adalah object biasa yang menyimpan tiga hal: referensi ke kode yang akan dijalankan, status eksekusi, dan nilai yang sedang ditunggu. Kode berikut membuktikannya:
import asyncio
async def sapa():
print("Halo dari dalam coroutine")
return "selesai"
coro = sapa()
print("nama fungsi:", coro.cr_code.co_name)
hasil = asyncio.run(coro)
print("nilai kembali:", hasil)Output yang Dihasilkan:
nama fungsi: sapa
Halo dari dalam coroutine
nilai kembali: selesaiMari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 7 (
coro = sapa()): interpreter membuat coroutine object tanpa menjalankan satu baris pun dari isisapa. - Baris 8 (
print("nama fungsi:", coro.cr_code.co_name)): atributcr_codemasih menunjuk ke kode functionsapa, sehingga tercetaksapa— coroutine menyimpan rencana eksekusinya, bukan hasilnya. - Baris 9 (
hasil = asyncio.run(coro)):asyncio.run()menggerakkan coroutinecorosampai selesai. Baru pada baris inilah"Halo dari dalam coroutine"dicetak. - Baris 10 (
print("nilai kembali:", hasil)): coroutine selesai denganreturn "selesai", dan nilai tersebut menjadi hasilasyncio.run().
Yang menggerakkan coroutine di dalam kode async adalah await. Perhatikan urutan berikut:
import asyncio
async def lapor(nomor):
print(f"laporan {nomor} dijalankan")
async def main():
coro = lapor(1)
print("coroutine dibuat, isi belum dijalankan")
await coro
print("coroutine selesai dijalankan")
asyncio.run(main())Output yang Dihasilkan:
coroutine dibuat, isi belum dijalankan
laporan 1 dijalankan
coroutine selesai dijalankanBaris coro = lapor(1) hanya membuat object. Isi lapor baru tereksekusi ketika await coro dijalankan. Dengan kata lain: await adalah penggerak coroutine — tanpa await (atau asyncio.run()), coroutine hanya barang jadi rencana yang tidak pernah bekerja.
Event Loop sebagai Penjadwal #
Siapa yang memutuskan coroutine mana yang maju? Jawabannya event loop: sebuah object penjadwal yang berjalan di satu thread dan terus melakukan siklus sederhana — pilih task yang siap, jalankan sampai task tersebut menyentuh await yang belum selesai, lalu pilih task berikutnya. Mirip pelayan restoran yang mengantar pesanan meja satu, lalu sambil menunggu masakan, mengantar pesanan meja lain. Batas analoginya: pelayan sejati bisa memutuskan pergi di tengah penyajian, sedangkan event loop tidak pernah menyela task — task-lah yang menyerahkan kontrol di await.
Karena hanya ada satu thread dan satu penjadwal, tidak ada dua baris coroutine yang berjalan pada saat yang sama, dan tidak ada task yang di-preempt di tengah baris. Seluruh pergantian terjadi di titik await. Konsekuensinya kita pelajari penuh pada Chapter Memahami async dan await.
asyncio.run: Titik Masuk yang Cukup #
Membuat dan mengelola event loop secara manual (asyncio.new_event_loop(), loop.run_forever()) adalah kode zaman lama. Sejak Python 3.7, satu function sudah mencukupi:
asyncio.run(main())membuat event loop baru,- membungkus
main()menjadi task dan menjalankannya sampai selesai, - lalu menutup event loop tersebut.
Pola baku program async adalah pola yang sudah kita pakai di seluruh chapter ini: definisikan async def main(), lalu panggil asyncio.run(main()) di baris terakhir skrip.
Dua Kesalahan Paling Umum #
Lupa await #
Coroutine yang dibuat tetapi tidak pernah di-await tidak menjalankan isinya, dan interpreter memberi tahu kita lewat RuntimeWarning ketika object tersebut dibersihkan:
async def sapa():
print("Halo dari coroutine")
sapa()
print("Program selesai")Output yang Dihasilkan (jalankan sebagai python3 lupa_await.py):
lupa_await.py:4: RuntimeWarning: coroutine 'sapa' was never awaited
sapa()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Program selesaiPeringatan muncul sebelum baris print("Program selesai") karena object hasil sapa() langsung dibuang begitu statement selesai. Python menampilkan path lengkap berkas di tempat Anda menjalankan skrip; path di sini dipendekkan agar ringkas. Isi pesannya jelas: coroutine 'sapa' tidak pernah di-await.
await di luar function async #
Keyword await hanya sah di dalam async def. Menuliskannya di level modul ditolak interpreter bahkan sebelum program berjalan:
import asyncio
async def ambil():
return 42
hasil = await ambil()Output yang Dihasilkan (jalankan sebagai python3 await_modul.py):
File "await_modul.py", line 6
hasil = await ambil()
^^^^^^^^^^^^^
SyntaxError: 'await' outside functionInterpreter menolak seluruh berkas dengan SyntaxError — sama seperti error sintaks lainnya, tidak ada satu baris pun yang dieksekusi.
Dengan coroutine, await, dan event loop yang kini punya wajah jelas, kita beranjak ke kotak peralatannya: modul asyncio beserta sleep, gather, wait_for, to_thread, dan Queue pada Chapter asyncio.