4

In python's asyncio asynchronous programming (version 3.7 or below), if I would like to manually let a coroutine gives back its control to the main event loop , I can use this code:

@asyncio.coroutine
def switch():
    yield
    return

async def task():
    # ...do something
    # ...
    await switch() # then this coroutine will be suspended and other will be triggered
    # ...
    # ... do something else when it's triggered again.

However in python3.8 "@coroutine" decorator is deprecated . And besides I could not use yield in a 'async def' (since it will define a async generator but not coroutine) . So how could I achive the same function?

AdamHommer
  • 657
  • 7
  • 22
  • 1
    Is there a reason why you want to implement your own ``switch`` coroutine? Practically all events loops consider their respective ``sleep(0)`` to do this. On top, not every event loop will respond properly to an empty ``yield``. – MisterMiyagi Mar 09 '20 at 15:19
  • `@asyncio.coroutine` is deprecated, but `@types.coroutine` isn't (and won't be), so you should use that. If you look carefully, `asyncio.sleep` uses that one internally as well. – user4815162342 Mar 09 '20 at 18:58

1 Answers1

5

TLDR: Do not use an explicit yield to switch coroutines. For asyncio, use asyncio.sleep(0) instead.


Practically all event loops consider their respective sleep with a duration of 0 to mean "let other coroutines run". For asyncio, use asyncio.sleep(0) to let other coroutines run.

async def task():
    # ...do something
    # ...
    await asyncio.sleep(0) # then this coroutine will be suspended and other will be triggered
    # ...
    # ... do something else when it's triggered again.

Sleeping (asyncio)

sleep() always suspends the current task, allowing other tasks to run.

Checkpoints (trio)

… it’s useful to know that await trio.sleep(0) is an idiomatic way to execute a checkpoint without doing anything else …

Time (curio)

Sleep for a specified number of seconds. If the number of seconds is 0, execution switches to the next ready task (if any).


If for some reason an explicit yield to the event loop is needed, create a custom class and yield in its __await__ special method.

class Switch:
    """Switch coroutines by yielding to the event loop"""
    def __await__(self):
        yield

Note that this sends a bare None to the event loop. Whether and how an event loop handles this signal depends on the async library used.

MisterMiyagi
  • 44,374
  • 10
  • 104
  • 119
  • Thanks bro ,using __await__method could achieve the same function.The reason why I'm disliking await asyncio.sleep(0) is that it's 10% slower than self define switcher...... – AdamHommer Mar 09 '20 at 15:48
  • I've looked into py3.8's source code implementation of asyncio.sleep(), it uses @types.coroutine decorator. Using this instead of asyncio.sleep(0) could speedup about 12% on my pc. – AdamHommer Mar 09 '20 at 16:03
  • should `async`be omitted in the line of `async def __await__(self):` – wangsquirrel Apr 15 '21 at 12:02
  • @wangsquirrel You are right, thanks for spotting this. Fixed now. – MisterMiyagi Apr 15 '21 at 12:09