[Python-Dev] Importance of "async" keyword

Steve Dower Steve.Dower at microsoft.com
Fri Jun 26 17:47:32 CEST 2015

On 06/26/2015 06:48 AM, Sven R. Kunze wrote:

> def business():
>      return complex_calc(5)
> def business_new()
>      return await complex_calc(10)

> Maybe, I completely missed the point of the proposal, but this is the way I would expect it to work. Putting in an 'await' whenever I see fit and it just works.

Assuming "async def business_new" (to avoid the syntax error), there's no difference between those functions or the one they're calling:

* "complex_calc" returns an awaitable object that, after you've awaited it, will result in an int.
* "business" returns the return value of "complex_calc", which is an awaitable object that, after you've awaited it, will result in an int.
* "business_new" returns an awaitable object that, after you've awaited it, will result in an int.

In all three of these cases, the result is the same. The fact that the awaitable object returned from any of them is implemented by a coroutine isn't important (in the same way that an iterable object may be implemented by a generator, but it's really irrelevant).

The value of the await syntax is when you're doing something interesting, a.k.a. your function is more than delegating directly to another function:

async def business_async():
    tasks = [complex_calc_async(i) for i in range(10)]
    results = [await t for t in tasks]
    await write_to_disk_async(filename, results)
    return sum(results)

Now it's actually useful to be able to await when we choose. Each call to complex_calc_async() could be starting a thread and then suspending until the thread is complete, so we actually start all 10 threads running here before blocking, and then collect the results in the order we started them (and not the order they complete, though I think asyncio has a function to do that). Without the explicit await this would be impossible.

The async def also lets us create coroutines consistently even if they don't await anything:

if has_version:
    async def get_version_async():
        return VERSION
    async def get_version_async():
        return (await get_major_version_async(), await get_minor_version_async())

async def show_version():
    print(await get_version_async())

If, like generators, regular functions became coroutines only in the presence of an await, we'd have to do convoluted code to produce the fast get_version_async(), or else make the caller worry about whether they can skip the await. (Also consider calls that cache their result - a coroutine MUST be awaited, but it doesn't have to await anything if it already has the result).

(Aside: the "_async" suffix is based on the convention used in C#, and it's certainly one that I'll be using throughout my async Python code and encouraging in any code that I review. It's the most obvious way to let callers know whether they need to await the function result or not.)


More information about the Python-Dev mailing list