4

I'm having difficulty to understand how the AsyncIOScheduler works and how I can schedule a function inside the main function, or how is the proper way to do that.

How can I run the function foo every 10 seconds?

Suppose I have this structure:

package/
    controllers.py
    main.py

From the file controllers.py I've got a function called foo and the function is something like this:

async def foo():
    print('Hello World')

I'd like to run the function foo (and many others) from the file main.py, and this file is like:

import asyncio
from controllers import foo, bar
from apscheduler.schedulers.asyncio import AsyncIOScheduler

async def main():

    # Init message
    print('\nPress Ctrl-C to quit at anytime!\n' )
    
    await asyncio.create_task(bar())

    scheduler = AsyncIOScheduler()
    scheduler.add_job(await asyncio.create_task(foo()), "interval", seconds=10)
    scheduler.start()

if __name__ == "__main__":
    while True:
        try:
            asyncio.run(main())
        except Exception as e:
            print("Exception: " + str(e))

Is it right to run the scheduler this way? Or the timer will be reseted every time the main function is running?

I tried the code below, the loop interval works, but the main function not.

import asyncio
from controllers import foo, bar
from apscheduler.schedulers.asyncio import AsyncIOScheduler

async def main():

    # Init message
    print('\nPress Ctrl-C to quit at anytime!\n' )
    
    await asyncio.create_task(bar())

if __name__ == "__main__":

    scheduler = AsyncIOScheduler()
    scheduler.add_job(foo, "interval", seconds=10)
    scheduler.start()

    while True:
        try:
            asyncio.get_event_loop().run_forever()
            asyncio.run(main())
        except Exception as e:
            print("Exception: " + str(e))

If I changed the order from:

            asyncio.get_event_loop().run_forever()
            asyncio.run(main())

to:

            asyncio.run(main())
            asyncio.get_event_loop().run_forever()

I get the error: There is no current event loop in thread 'MainThread'.

The loop works (scheduler only), but the main function is not running, how can I put those together in loop?

Guilherme Matheus
  • 573
  • 10
  • 30
  • The expression `await asyncio.create_task(foo())` does two things: (1) it creates a task from the coroutine foo(); and (2) runs it to completion. The value of the expression is the returned value from foo(), which is `None`. That's the value that you are passing to the function `scheduler.add_job()`. That can't be right. You should check the docs for add_job and be sure you are passing the right kind of object to it. – Paul Cornelius Jan 04 '22 at 20:20
  • @PaulCornelius Got it! Actually it was just an example, my point was, is it right to run the scheduler that way from the `main` function? Or the timer will be reseted every time the `main` function is running? I'd like to run the function `foo` every 10 seconds, but from another file. – Guilherme Matheus Jan 04 '22 at 20:50
  • I'm sure it won't matter which source file foo lives in. The basics of Python programming haven't changed because of asyncio :-). As far as the scheduler is concerned, I'm the wrong person to comment on that because I don't use that module. I just happened to notice the problem with the add_job call. – Paul Cornelius Jan 04 '22 at 23:59

1 Answers1

5

I suppose you want to run you main() function forever, so you can try it in this way.

controllers.py

from datetime import datetime


async def foo():
    print(f'{datetime.now()} Foo')


async def bar():
    print(f'{datetime.now()} Bar')

main.py

import asyncio
from controllers import foo, bar
from apscheduler.schedulers.asyncio import AsyncIOScheduler


async def main():
    # Init message
    print('\nPress Ctrl-C to quit at anytime!\n')

    scheduler = AsyncIOScheduler()
    scheduler.add_job(foo, "interval", seconds=2)
    scheduler.start()
    
    while True:
        await asyncio.create_task(bar())
        await asyncio.sleep(1)


if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    loop.create_task(main())
    loop.run_forever()

Output:

Press Ctrl-C to quit at anytime!
2022-01-09 03:38:10.468914 Bar
2022-01-09 03:38:11.472966 Bar
2022-01-09 03:38:12.468882 Foo
2022-01-09 03:38:12.473449 Bar
2022-01-09 03:38:13.477678 Bar
2022-01-09 03:38:14.469389 Foo
2022-01-09 03:38:14.480098 Bar
2022-01-09 03:38:15.483889 Bar
2022-01-09 03:38:16.472584 Foo
2022-01-09 03:38:16.487105 Bar
2022-01-09 03:38:17.492550 Bar
2022-01-09 03:38:18.467403 Foo
2022-01-09 03:38:18.493330 Bar
2022-01-09 03:38:19.496680 Bar
2022-01-09 03:38:20.471553 Foo
2022-01-09 03:38:20.499928 Bar
2022-01-09 03:38:21.503572 Bar
2022-01-09 03:38:22.470329 Foo
2022-01-09 03:38:22.505597 Bar
2022-01-09 03:38:23.510854 Bar
2022-01-09 03:38:24.471455 Foo
...

Also, you can run periodic functions without using AsyncIOScheduler like this.

Oleksii Tambovtsev
  • 2,666
  • 1
  • 3
  • 21
  • I raised into 5 seconds to test it, and mine output gets only `Foo` results after the first `Bar` ouput (same as yours apparently. Shouldn't we get `Bar` outputs while we don't get `Foo` in the meantime? 2022-01-08 19:47:13.016932 Bar 2022-01-08 19:47:18.143549 Foo 2022-01-08 19:47:23.151487 Foo 2022-01-08 19:47:28.134608 Foo 2022-01-08 19:47:33.143508 Foo – Guilherme Matheus Jan 08 '22 at 22:49
  • @GuilhermeMatheus could you specify your expected output? – Oleksii Tambovtsev Jan 08 '22 at 23:16
  • I'd expect that `Bar` function keeps running until forever, while the function from `scheduler` (`Foo`) runs after interval reaches over and over (which was 5 seconds in this case). – Guilherme Matheus Jan 09 '22 at 01:30
  • 1
    @GuilhermeMatheus Updated my answer. Is that what you expect? – Oleksii Tambovtsev Jan 09 '22 at 01:40
  • 1
    Exactly that, I really appreciate, thank you! – Guilherme Matheus Jan 09 '22 at 01:43
  • My pleasure, sir. – Oleksii Tambovtsev Jan 09 '22 at 01:47
  • I've got a question, is it possible to no keep `While True` in `bar` function and do another approach? I mean, what if I want that `bar` function to finish, but the `main` function keeps running forever, so `bar` will be called forever aswell. – Guilherme Matheus Jan 10 '22 at 03:28
  • I may have misunderstood your question, but why don't you just move `While True` to the `main` function (I updated my answer)? Now `bar` function finishes, `main` function running forever, because `loop.run_forever()` run the event loop until some coroutine or callback invokes loop.stop(), and `main` function generates and executes `bar` tasks forever. – Oleksii Tambovtsev Jan 10 '22 at 16:29
  • Actually you understood correctly, I was just asking another way to get to the solution, and this new edited answer is much better! This is a difficult package to understand for me yet. I'm working on a NFT game bot for the first time, so you are really helping! – Guilherme Matheus Jan 10 '22 at 17:11