8.6. ข้อยกเว้น#
ข้อยกเว้นภายในสคริปต์ asyncio ทำงานเกือบเหมือนกับใน Python ทั่วไป มันแพร่กระจายขึ้นไปตามสายการเรียกจนกว่าบางสิ่งจะจับมัน เกือบ เพราะงานทำงานพร้อมกัน ดังนั้นเส้นทาง "ขึ้น" ไม่ใช่เส้นทางที่สร้างงาน หน้านี้ครอบคลุมว่าข้อยกเว้นไปที่ไหนในแต่ละรูปแบบทั่วไป
8.6.1. ภายในคอรูทีนเดียว#
try/except ภายในคอรูทีนจับข้อยกเว้นที่ถูกยกขึ้นโดยสิ่งที่มัน awaitในแบบปกติ:
async def fetch_with_retry(url):
for attempt in range(3):
try:
return await fetch(url)
except OSError as e:
last_error = e
raise last_error
ไม่มีอะไรเฉพาะ asyncio ที่นี่ ส่วน except เห็นข้อยกเว้นที่ถูกยกขึ้นภายใน fetch เหมือนกับที่มันเป็นการเรียกฟังก์ชันปกติ
8.6.2. ในงานที่แอปพลิเคชันกำลัง await#
เมื่อคอรูทีนที่ทำงานเป็น Task ยกข้อยกเว้น ข้อยกเว้นนั้นจะถูกเก็บไว้ในงาน ครั้งถัดไปที่บางสิ่ง awaitงานนั้น ข้อยกเว้นจะถูกยกขึ้นใหม่ที่ await
task = asyncio.create_task(may_fail())
try:
result = await task
except OSError:
log("may_fail failed")
เช่นเดียวกันนี้ใช้กับ asyncio.gather() พฤติกรรมเริ่มต้น ซึ่งก็คือ ลูกตัวหนึ่งยก ตัวอื่นถูกยกเลิก ข้อยกเว้นแพร่กระจายออกจาก gather มาจากกลไกนี้
8.6.3. ในงานที่ไม่มีใคร await#
งานที่ไม่มีใคร awaitเลยคือกรณีที่ต้องใส่ใจ ข้อยกเว้นยังคงเกิดขึ้น loop สังเกตว่างานเสร็จสิ้นพร้อมข้อยกเว้นที่ไม่ได้รับการจัดการ แต่ไม่มี await ให้มันปรากฏ พฤติกรรมเริ่มต้นคือพิมพ์ traceback ผ่าน sys.stderr และทำงานต่อไป ซึ่งเหมาะสำหรับการวินิจฉัยที่ไม่มีคนดูแล แต่ไม่เหมาะกับแอปพลิเคชันที่ต้องการทราบ
การแก้ไขที่ถูกต้องมักคือการ await งาน นั้น ไม่ว่าจะโดยตรงโดยจำตัวจัดการและ await ระหว่างการปิด หรือโดยปริยายผ่าน gather() หรือ wait_for() รูปแบบ "การปิดแอปพลิเคชัน" ของหน้า การหมดเวลาและการยกเลิก จัดการกรณีนี้สำหรับงานพื้นหลังที่ยาวนานที่สคริปต์ทั่วไปสร้างขึ้น
8.6.4. ตัวจัดการข้อยกเว้นแบบกำหนดเอง#
เมื่อการพิมพ์ traceback และดำเนินการต่อไปอย่างเงียบๆ ไม่เพียงพอ loop จะเปิดเผย hook ซึ่งก็คือ Loop.set_exception_handler ที่แอปพลิเคชันสามารถแทนที่เพื่อทำอะไรบางอย่างแทน:
def handler(loop, context):
print("asyncio:", context.get("message"))
if "exception" in context:
sys.print_exception(context["exception"])
loop = asyncio.get_event_loop()
loop.set_exception_handler(handler)
อาร์กิวเมนต์ context คือ dict ที่มีคีย์ 'message', 'exception' และ 'future' ข้อยกเว้นอาจขาดหายไปในเหตุการณ์แบบคำเตือนบางอย่าง นั่นเป็นเหตุผลที่ตัวอย่างใช้ .get()
การใช้งานทั่วไปคือการบันทึกความล้มเหลวไปยังแฟลช กะพริบ LED ข้อผิดพลาด หรือส่งต่อไปยังการรีบูต watchdog หน้า loop control ครอบคลุมพื้นผิวทั้งหมดของ loop hooks
8.6.5. KeyboardInterrupt#
เมื่อสคริปต์ถูกหยุดจากภายนอก ซึ่งโดยปกติคือ IDE ที่ขอให้หยุด คำขอจะมาถึงภายในสคริปต์เป็น KeyboardInterrupt ภายใน asyncio.run() มันแพร่กระจายในแบบเดียวกับข้อยกเว้นที่ไม่ได้รับการจัดการอื่นๆ: main ถูกยกเลิก งานทุกชิ้นที่ loop ติดตามอยู่ก็ถูกยกเลิกด้วย และ KeyboardInterrupt ถูกยกขึ้นใหม่ออกจาก asyncio.run() ส่วน finally ทำงานระหว่างออก ดังนั้นรูปแบบการทำความสะอาดเดียวกับหน้าการยกเลิกคือสิ่งที่จัดการมัน