How can I run an async function using the schedule library?

后端 未结 5 943
广开言路
广开言路 2020-11-29 10:52

I\'m writing a discord bot using discord.py rewrite, and I want to run a function every day at a certain time. I\'m not experienced with async functions at all and I can\'t

5条回答
  •  刺人心
    刺人心 (楼主)
    2020-11-29 11:13

    What you're doing doesn't work because do takes a function (or another callable), but you're trying to await or call a function, and then pass it the result.

    await send_channel() blocks until the send finishes and then gives you None, which isn't a function. send_channel() returns a coroutine that you can await later to do some work, and that isn't a function either.

    If you passed it just send_channel, well, that is a function, but it's an ascynio coroutine function, which schedule won't know how to run.


    Also, rather than trying to integrate schedule into the asyncio event loop, and figure out how to wrap async jobs up as schedule tasks and vice versa and so on, it would far easier to just give schedule its own thread.

    There's a FAQ entry on this:

    How to continuously run the scheduler without blocking the main thread?

    Run the scheduler in a separate thread. Mrwhick wrote up a nice solution in to this problem here (look for run_continuously()).

    The basic idea is simple. Change your timer function to this:

    schedstop = threading.Event()
    def timer():
        while not schedstop.is_set():
            schedule.run_pending()
            time.sleep(3)
    schedthread = threading.Thread(target=timer)
    schedthread.start()
    

    Do this at the start of your program, before you even start your asyncio event loop.

    At exit time, to stop the scheduler thread:

    schedstop.set()
    

    Now, to add a task, it doesn't matter whether you're in your top-level code, or in an async coroutine, or in a scheduler task, you just add it like this:

    schedule.every().day.at("21:57").do(task)
    

    Now, back to your first problem. The task you want to run isn't a normal function, it's an asyncio coroutine, which has to be run on the main thread as part of the main event loop.

    But that's exactly what call_soon_threadsafe is for. What you want to call is:

    bot.loop.call_soon_threadsafe(send_channel)
    

    To ask scheduler to run that, you just pass bot.loop.call_soon_threadsafe as the function and send_channel as the argument.

    So, putting it all together:

    schedule.every().day.at("21:57").do(
        bot.loop.call_soon_threadsafe, send_channel)
    

提交回复
热议问题