Using asyncio#

How do I run two coroutines in parallel?#

You must start two tasks, which the event loop will run concurrently. You can achieve this with asyncio.gather() or asyncio.create_task().

Keep track of the tasks and make sure they terminate or you cancel them when the connection terminates.

Why does my program never receive any messages?#

Your program runs a coroutine that never yields control to the event loop. The coroutine that receives messages never gets a chance to run.

Putting an await statement in a for or a while loop isn’t enough to yield control. Awaiting a coroutine may yield control, but there’s no guarantee that it will.

For example, send() only yields control when send buffers are full, which never happens in most practical cases.

If you run a loop that contains only synchronous operations and a send() call, you must yield control explicitly with asyncio.sleep():

async def producer(websocket):
    message = generate_next_message()
    await websocket.send(message)
    await asyncio.sleep(0)  # yield control to the event loop

asyncio.sleep() always suspends the current task, allowing other tasks to run. This behavior is documented precisely because it isn’t expected from every coroutine.

See issue 867.

Why am I having problems with threads?#

If you choose websockets’ default implementation based on asyncio, then you shouldn’t use threads. Indeed, choosing asyncio to handle concurrency is mutually exclusive with threading.

If you believe that you need to run websockets in a thread and some logic in another thread, you should run that logic in a Task instead. If it blocks the event loop, run_in_executor() will help.

This question is really about asyncio. Please review the advice about Concurrency and Multithreading in the Python documentation.

Why does my simple program misbehave mysteriously?#

You are using time.sleep() instead of asyncio.sleep(), which blocks the event loop and prevents asyncio from operating normally.

This may lead to messages getting send but not received, to connection timeouts, and to unexpected results of shotgun debugging e.g. adding an unnecessary call to send() makes the program functional.