Accueil / Blog / Métier / 2015 / Python async/await: introduction

Python async/await: introduction

Par Alex Marandon publié 10/07/2015, édité le 23/01/2016
Python 3.5 is coming up soon with async and await built-in keywords. Let's get excited with a couple of simple examples showing off how to use this new syntax.

For those unaware of asynchronous programming, we could say it's a way to achieve a certain level of multi-tasking within a single process or thread. It leverages the fact that our typical processes spend a lot of their time waiting for input/ouput (I/O) operations to complete. The kind of operation involving I/O includes receiving requests from clients, reading files, querying a database server or talking to a RESTful API. Instead of waiting for each I/O operation to complete before starting the next one, asynchronous programming allow a single process to schedule several such operations in parallel.

This is similar to how you may prepare your breakfast. You probably don't wait for coffee or tea to be ready before starting your toaster or rice cooker. You start both operations in parallel and get notified by an event (kettle whistling, bread popping out) when they've completed. This way you can multitask effectively even though you're a single "process".

Let's start with a Hello World example using asyncio.sleep to simulate slow I/O operations and asyncio.wait to launch them in parallel. This is how it would look like before Python 3.5:

import asyncio

@asyncio.coroutine
def slow_operation(n):
    yield from asyncio.sleep(1)
    print("Slow operation {} complete".format(n))


@asyncio.coroutine
def main():
    yield from asyncio.wait([
        slow_operation(1),
        slow_operation(2),
        slow_operation(3),
    ])


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

As you can see, this example uses existing Python syntax: decorators and the yield keyword.

Running this script gives us this output:

$ python3.4 sleeping.py
Slow operation 1 complete
Slow operation 2 complete
Slow operation 3 complete

Now here is how the code may look like using the upcoming version 3.5 of Python:

import asyncio

async def slow_operation(n):
    await asyncio.sleep(1)
    print("Slow operation {} complete".format(n))


async def main():
    await asyncio.wait([
        slow_operation(1),
        slow_operation(2),
        slow_operation(3),
    ])


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

The new async and await keywords make it more clear that we're dealing with asynchronous code rather than regular generators.

Let's run this script (with Python 3.5):

$ python3.5 sleeping-awaitasync.py
Slow operation 2 complete
Slow operation 3 complete
Slow operation 1 complete

Now for a slightly more realistic example, we'll implement a web server that makes sub-requests to external HTTP services. We could imagine it evolving into a website monitoring app. We'll use aiohttp, an HTTP client/server for asyncio:

import asyncio
import aiohttp
from aiohttp import web
import json

WEBSITES = ['http://example.com/', 'http://dummy-a98x3.org', 'http://example.net/']

async def handle(request):
    # Fire up 3 requests in parallel
    coroutines = [aiohttp.request('get', website) for website in WEBSITES]

    # Wait for those requests to complete
    results = await asyncio.gather(*coroutines, return_exceptions=True)

    # Analyze results
    response_data = {
        website: not isinstance(result, Exception) and result.status == 200
        for website, result in zip(WEBSITES, results)
    }

    # Build JSON response
    body = json.dumps(response_data).encode('utf-8')
    return web.Response(body=body, content_type="application/json")

loop = asyncio.get_event_loop()
app = web.Application(loop=loop)
app.router.add_route('GET', '/', handle)

server = loop.create_server(app.make_handler(), '127.0.0.1', 8000)
print("Server started at http://127.0.0.1:8000")
loop.run_until_complete(server)
try:
    loop.run_forever()
except KeyboardInterrupt:
    pass

Let's start the server:

$ python3.5 server.py
Server started at http://127.0.0.1:8000

And send it a request:

$ curl http://127.0.0.1:8000
{"http://example.com/": true, "http://example.net/": true, "http://dummy-a98x3.org": false}

That's it for today. I hope this got you excited about the new Python async/await syntax. Now go read the spec or grab the pre-releasse and have fun!

ABONNEZ-VOUS À LA NEWSLETTER !
Voir aussi
Récupérer des données Hadoop avec Python 02/05/2017

Accéder à des données situées sur un cluster Hadoop peut se faire de différentes manières en ...

Les curseurs PostgreSQL 11/06/2015

Découvrons comment utiliser les curseurs PostgreSQL pour effectuer des requêtes renvoyant de ...

Présentation de l'écosystème Python scientifique Présentation de l'écosystème Python scientifique 10/11/2016

Au fil des années Python est devenu un outil du quotidien pour les ingénieurs et chercheurs de ...

Formation Python avancé du 12 au 16 juin à Paris Formation Python avancé du 12 au 16 juin à Paris 12/04/2017

Pour les développeurs qui veulent approfondir leur connaissance du langage Python : de la ...

Python : Bien configurer son environnement de développement Python : Bien configurer son environnement de développement 07/12/2015

Comment utiliser les bonnes pratiques de développement Python.