8.3. Vòng lặp sự kiện

Vòng lặp sự kiện là động cơ mà asyncio chạy phía dưới. Nó giữ danh sách mọi task trong chương trình, yêu cầu mỗi task chạy cho đến await tiếp theo của task đó, rồi chuyển sang task sẵn sàng tiếp theo. Khi không có task sẵn sàng, nó chờ -- việc chờ thực sự là điều khiến CPU có thể dùng để firmware chạy các thứ khác và để các giấc ngủ tiết kiệm điện bắt đầu -- cho đến khi điều gì đó mà một task đang chờ trở nên khả dụng, rồi tiếp tục task đó. Lặp lại mãi mãi.

Hầu hết các ứng dụng không bao giờ tương tác trực tiếp với vòng lặp. Vòng lặp là hệ quả của việc gọi asyncio.run(); ứng dụng viết coroutine, lên lịch chúng như các task, và vòng lặp làm phần còn lại.

8.3.1. asyncio.run() thực sự làm gì

Một lần gọi duy nhất:

asyncio.run(main())

là viết tắt cho một chuỗi dài hơn mà vòng lặp quản lý thay cho ứng dụng:

  1. Tạo vòng lặp sự kiện nếu nó chưa tồn tại.

  2. Bọc coroutine được cung cấp trong một task và lên lịch nó làm điểm vào cấp cao nhất của vòng lặp.

  3. Chạy vòng lặp -- bước qua các task sẵn sàng, chờ khi không có task nào sẵn sàng, tiếp tục các task khi await của chúng hoàn thành -- cho đến khi task cấp cao nhất trả về hoặc phát ra ngoại lệ.

  4. Hủy bất kỳ task nào mà ứng dụng đã tạo vẫn đang chạy.

  5. Trả về bất cứ điều gì coroutine cấp cao nhất đã trả về (hoặc phát lại bất cứ ngoại lệ nào nó đã phát).

8.3.2. Mỗi chương trình một vòng lặp

Asyncio của MicroPython có một vòng lặp sự kiện, hoàn toàn. Không có việc tạo một vòng lặp mới, và không có việc lồng một vòng lặp vào một vòng lặp khác. Gọi asyncio.run() từ bên trong một coroutine đang chạy trên vòng lặp là lỗi; vòng lặp đã ở đó, và coroutine chỉ cần await bất cứ thứ gì nó muốn bắt đầu.

Trong thực tế quy tắc giống với dòng cuối của trang trước: có chính xác một lần gọi asyncio.run() mỗi chương trình, ở trên cùng, với một async def main() đơn ở phía sau. Mọi thứ khác sống bên trong main.

8.3.3. Truy cập vòng lặp trực tiếp

Đối với các trường hợp hiếm hoi mà ứng dụng cần chạm đến chính vòng lặp -- chủ yếu là chẩn đoán và xử lý ngoại lệ -- asyncio.get_event_loop() trả về đối tượng Loop. Từ đó ứng dụng có thể cài đặt trình xử lý ngoại lệ tùy chỉnh, kiểm tra vòng lặp đang làm gì, hoặc (hiếm khi) gọi create_task() trực tiếp thay vì asyncio.create_task() (chúng là cùng một thao tác).

Toàn bộ tập phương thức mà Loop cung cấp -- run_forever(), stop(), set_exception_handler(), và phần còn lại -- được đề cập trong trang kiểm soát vòng lặp ở phần sau của mục này. Cho đến lúc đó, asyncio.run(main()) là tất cả những gì ứng dụng cần.