0
votes

I am making a Discord bot in Python. What I want to do at one point is to await two coroutines, and once the first has completed run the rest of the code but cancel the remaining coroutine (unlike here where I just had one coroutine and wanted it to be cancelled after a specific time.)

Here is the minimum reproducible example:

import asyncio
import discord

client = discord.Client()

async def testfunc():
    await asyncio.sleep(10)
    print("10 seconds.")

async def testfunc2():
    await asyncio.sleep(15)
    print("15 seconds.")

@client.event
async def on_ready():
    print("Connected!")
    await asyncio.wait([testfunc(),testfunc2()],return_when=asyncio.FIRST_COMPLETED)

client.run('MY BOT TOKEN')

What I intend to happen in this minimal example is for it to print "10 seconds." after 10 seconds and then not print anything else. However, what actually happens is that it also prints "15 seconds." after 15 seconds, and I want to cancel that coroutine so that does not happen.

It appears that I cannot use asyncio.wait_for in this case as there are two coroutines instead of one, both of which must run simultaneously until one finishes. Since asyncio.wait does not cancel coroutines after one has finished, is there any way to cancel them?

1

1 Answers

2
votes

If you wrap the coroutines in tasks, you can cancel the ones that haven't finished

@client.event
async def on_ready():
    print("Connected!")
    tasks = [asyncio.create_task(testfunc()), asyncio.create_task(testfunc2())]
    await asyncio.wait(tasks,return_when=asyncio.FIRST_COMPLETED)
    for task in tasks:
        if not task.done():
            task.cancel()