What is "await" for in Python?

Asked

Viewed 3,091 times

12

I’d like to know what it is and what it’s for await in Python.

Has something to do with threads?

  • In C# it would be a command to wait for the task of Task(new thread). I think it would be the same schema.

1 answer

15


The await Python is used so that it looks like a prefix for a function call that will be an asynchronous call.

So, starting at the end - what we see in the code and its effect, and then understanding what actually happens. You may find code that way:

async def uma_funcao():
    # algum código preparatório
    ...
    resultado = await outra_funcao(valores)
    # algumas linhas de código usando o resultado acima 
    ...
    return valor

What happens there is that if the outra_funcao If it takes too long, your code will suffer a "pause" at that point, and then continue at the bottom line, as if nothing had happened. Pera - but that’s not what would happen in a normal program without the await? The difference is that in code of this type, while the code of uma_funcao awaits the outcome of the call to outra_funcao, the program not to . Other functions and other code declared as "asynchronous" continues running.

So the await and other key words (such as async def also present in the example above), were included in the language to facilitate the use of this programming approach called "asynchronous". (Asynchronous programming was already possible before the use of these keywords in Python 3.5, but the thing was done by hand). Asynchronous programming, in turn, allows a given program to do more than one thing "apparently" at the same time - that is, in a given instant, there may be more than one function of the program code being executed - and in that, it is similar to multi-threaded programming.

When using multi-threading, however, there are some differences: threads can (though not the rule in Python), use more than one physical core of the processor, and be executed "in fact" at the same time. In asynchronous programming, all the action takes place in a single system thread (and therefore a single CPU core). Function calls prefixed with await (and some commands prefixed with async, such as async for and async with) cause a "pause" in the function that is running - and the control of the execution is passed to a control function - in general we call "loop" (loop) asynchronous system control. This "loop" is what manages the interruption and continuity of various functions that take breaks.

To give a very concrete example, of a restaurant: a waiter working with a normal "synchronous" code: he would go to the table, pick up the client’s order, go to the kitchen, deliver the order and waiting stopped until the order is ready. When it is ready, take the order to the original table and only there will meet other tables.

The "multi-threading" waiter does exactly the same thing - only the restaurant has more than one waiter. So if the restaurant has 10 tables and 5 waiters, the first 5 tables won’t even notice - they call a waiter, he comes, and a few minutes later he comes back with the order ready. The sixth table customer will see 5 waiters standing in the kitchen while they wish to order, and may be upset.

The "asynchronous" waiter: goes to the table, takes the order, goes to the kitchen, delivers the order - this is equivalent to the "await". He returns to the lounge, attends other tables, and eventually, when he goes to the kitchen (or is called by the bell), picks up the result of the original order and takes it to that table. For those who were at the table, the whole process took the same time, but the waiter can serve several other tables while the order was prepared.

One of the main advantages of this approach is that you, when writing the asynchronous function, know exactly at what point the code of your function will be "paused" and another code will run - so if your program is using shared data structures (such as lists or dictionaries), you know that if between two lines of your code there is no so-called "async"no other part of the code was executed, and therefore its data structures were not changed by other functions. This makes asynchronous code not need to make use of the locks and mutexes used in multi-threaded code (via rule). Another advantage is that there is no expense of thread exchange resources for passing data between two different tasks.

The important thing to think about writing code with this approach however is to know that it is only possible to have real gains if you have some function that would be "blocking" - for example, a call that will request a remote API, or request a time-consuming query to a database - that call must itself be within an asynchronous function (function that has the async def instead of only def) , then the main loop will be able to execute more code while the result of the time consuming function does not arrive. In the case of access to the database, or the file system, it is necessary that the Python driver itself is asynchronous (i.e., to use postgresql, you will connect to the database with "asyncpg", not with the popular "psycopg2", in order to have asynchronous programming benefits.

As a rule, this type of code makes more sense in programs that will function as servers (including WEB applications), serving several clients at the same time: while a request is being processed, if you find a lengthy call to gather the data needed for the response, other requests can be processed in parallel. But it is possible to think of writing the main core of a game in which the various agents are coordinated through asynchronous functions as well.

On the other hand, precisely because this type of code is only beginning to pay off in complex applications, it is very difficult to find a concrete and small example. The documentation includes a "hello world" - all it uses is an asynchronous "Sleep" function that takes a little longer to answer -

import asyncio

async def say(what, when):
    await asyncio.sleep(when)
    print(what)
    if when == 5:
       loop = asyncio.get_event_loop()
       loop.stop()

loop =  asyncio.new_event_loop()
for i in range(5, -1, -1):
    loop.create_task(say(f'hello world {i}', i))
    print(f"co-rotina {i} criada")
loop.run_forever()
loop.close()

(based on the example in http://asyncio.readthedocs.io/en/latest/hello_world.html)

So to summarize what happens there: the Python language has a very complete implementation of such "orchestration loop" in the library asyncio. Nothing prevents you from implementing your own loop to make use of the same keywords.

The call loop = asyncio.new_event_loop() takes a reference to a new coordination loop. The call loop.create_task(say('hello world', 1)) adds to the "list of tasks being executed" in the loop that call to the function say.

Note that unlike when we pass a function to be executed on a separate thread, where we pass the function name (without the ( ) to call the function) and the arguments separately, the call to run_until_complete calls the function say : say('hello world', 1). What happens is how it was defined with async def it is not executed at this time - it is only prepared, and Python creates an object of type "co-routine". This "co-routine" that is placed in the list of tasks of the loop - and then he "turns" to orchestrate the calls and real results.

Who programs the functions do not need to worry about checking results of futures or what to do if an exception happens inside an asynchronous call - the main loop of the event sends the exceptions to the right point in the program.

So if you paste the above code into the interactive interpreter you’ll see by the prints that all the tasks are created at the same time - but each one "sleeps" a different number of seconds - and the results are coming in one per second. If you make the equivalent code using time.sleep, each function will stop when seconds, before continuing - after printing "4", the program would wait 5 whole seconds for the next answer.

  • I’m not very clear on one thing, to use the await in Python, is necessary be within an asynchronous function (async def...) as in javascript?

  • yes, it is. Without being in an asynchronous function, there is no external loop that can take control when await is used.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.