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 ทำงานระหว่างออก ดังนั้นรูปแบบการทำความสะอาดเดียวกับหน้าการยกเลิกคือสิ่งที่จัดการมัน