42

The documentation for asyncio gives two examples for how to print "Hello World" every two seconds: https://docs.python.org/3/library/asyncio-eventloop.html#asyncio-hello-world-callback https://docs.python.org/3/library/asyncio-task.html#asyncio-hello-world-coroutine

I can run those from the interpreter, but if I do I lose access to the interpreter. Can an asyncio event loop be run in the background, so that I can keep typing commands at the interpreter?

dano
  • 91,354
  • 19
  • 222
  • 219
Jeffrey Benjamin Brown
  • 3,427
  • 2
  • 28
  • 40

2 Answers2

63

Edit:

If using Python 3.8 or above, you should use the asyncio repl, as explained in zeronone's answer. If using 3.7 or lower, you can use this answer.


You can run the event loop inside a background thread:

>>> import asyncio
>>> 
>>> @asyncio.coroutine
... def greet_every_two_seconds():
...     while True:
...         print('Hello World')
...         yield from asyncio.sleep(2)
... 
>>> def loop_in_thread(loop):
...     asyncio.set_event_loop(loop)
...     loop.run_until_complete(greet_every_two_seconds())
... 
>>> 
>>> loop = asyncio.get_event_loop()
>>> import threading
>>> t = threading.Thread(target=loop_in_thread, args=(loop,))
>>> t.start()
Hello World
>>> 
>>> Hello World

Note that you must call asyncio.set_event_loop on the loop, otherwise you'll get an error saying that the current thread doesn't have an event loop.

If you want to interact with the event loop from the main thread, you'll need to stick to loop.call_soon_threadsafe calls.

While this kind of thing is an ok way to experiment in the interpreter, in actual programs, you'll probably want all your code running inside the event loop, rather than introducing threads.

dano
  • 91,354
  • 19
  • 222
  • 219
  • 2
    Thank you so much. Someone [elsewhere](http://monome.org/community/discussion/comment/208744#Comment_208744) said not to mix threads and asyncio. Do you disagree? The process would background is an OSC server. From the interpreter I would tell it what signals to send. The Python process would be lightweight, sending instructions (maybe 20 bundles a second) to synthesizers that do all the audio math. Given that motivation, do you continue to suggest putting everything in the main loop? – Jeffrey Benjamin Brown Oct 09 '14 at 06:36
  • 1
    @user916142 Well, normally the whole point of using an asynchronous framework like `asyncio` is to *avoid* using threads. If you're always going to run this code inside an interactive interpreter, then I guess it makes sense. But normally I would say you should implement an `asyncio` `Procotol` or `Stream` that handles incoming network connections, and then tell it what signals to send via network requests. – dano Oct 09 '14 at 14:48
  • Thanks! I've got something much like user916142, with threads handling OSC, Midi, WebSockets, and serial data streams. I'll look into the possibility of rolling it all into an event loop, but for now just needed to integrate a WebSockets module which depended on asyncio. – Joseph Sheedy Mar 12 '15 at 07:20
  • 1
    [Here](https://github.com/jsheedy/neopixel-framebuffer/blob/1ba1fcb2d8859edaa96a948c03cf50e94477ae8a/neopixel_framebuffer.py#L56) is some of my code that handles messages over serial, WebSockets, stdin, and OSC with asyncio. It doesn't give you access to the interpreter, but my solution was to handle keyboard input with a callback: loop.add_reader(sys.stdin.fileno(), read_stdin) – Joseph Sheedy Dec 08 '15 at 02:41
  • 6
    @dano "you'll probably want *all* your code running inside the event loop, rather than introducing threads" -- Would you make an exception for a situation where you have large codebase that you can't / don't want to convert entirely to async style; and at the same time, you do want some capabilities of `asyncio` (such as scheduled background operations)? It seems running an event loop in a background thread would fit perfectly in this scenario, but I may be missing some hidden complications. – max May 18 '17 at 10:05
  • 2
    @max Yes, I think that's ok. You just need to be careful about sharing data between the `asyncio` eventloop thread and whatever other threads you have running. – dano May 18 '17 at 13:29
39

With Python 3.8, you can use the new asyncio REPL.

$ python -m asyncio
>>> async def greet_every_two_seconds():
...     while True:
...         print('Hello World')
...         await asyncio.sleep(2)
...
>>> # run in main thread (Ctrl+C to cancel)
>>> await greet_every_two_seconds()
...
>>> # run in background
>>> asyncio.create_task(greet_every_two_seconds())
zeronone
  • 2,912
  • 25
  • 28
  • I get "SyntaxError: invalid syntax" pointing at the from in "await from asyncio.sleep(2). I'm currently running python 3.8 – avenmia Feb 23 '20 at 23:24
  • 3
    @avenmia remove the `from` after `await` – william_grisaitis Jun 26 '20 at 02:28
  • 4
    RuntimeError: no running event loop. Looks like one needs to create a new loop with loop = asyncio.get_event_loop() and then task = loop.create_task(greet_every_two_seconds()) – Tobbey Oct 05 '21 at 16:44