Depuis Python 3.5, les mots clés await
et async
sont introduits dans le langage. Maintenant, je suis plutôt une personne Python 2.7 et j'évite Python 3 depuis un certain temps, donc asyncio
est assez nouveau pour moi. D'après ma compréhension, il semble que await/async
Fonctionne de manière très similaire à la façon dont ils fonctionnent dans ES6 (ou JavaScript, ES2015, mais vous voulez l'appeler.)
Voici deux scripts que j'ai faits pour les comparer.
import asyncio
async def countdown(n):
while n > 0:
print(n)
n -= 1
await asyncio.sleep(1)
async def main():
"""Main, executed in an event loop"""
# Creates two countdowns
futures = asyncio.gather(
countdown(3),
countdown(2)
)
# Wait for all of them to finish
await futures
# Exit the app
loop.stop()
loop = asyncio.get_event_loop()
asyncio.ensure_future(main())
loop.run_forever()
function sleep(n){
// ES6 does not provide native sleep method with promise support
return new Promise(res => setTimeout(res, n * 1000));
}
async function countdown(n){
while(n > 0){
console.log(n);
n -= 1;
await sleep(1);
}
}
async function main(){
// Creates two promises
var promises = Promise.all([
countdown(3),
countdown(2)
]);
// Wait for all of them to finish
await promises;
// Cannot stop interpreter's event loop
}
main();
Une chose à noter est que les codes sont très similaires et fonctionnent à peu près de la même manière.
Voici les questions:
Dans Python et ES6, await/async
Sont basés sur des générateurs. Est-il correct de penser que les Futures sont les mêmes que les Promesses?
J'ai vu les termes Task
, Future
et Coroutine
utilisés dans la documentation asyncio
. Quelles sont les différences entre eux?
Dois-je commencer à écrire du code Python qui a toujours une boucle d'événements en cours d'exécution?
- Dans les deux Python et ES6,
await/async
Sont basés sur des générateurs. Est-il correct de penser que les Futures sont les mêmes que les Promesses?
Pas Future
, mais Task
de Python est à peu près équivalent à Promise
de Javascript. Voir plus de détails ci-dessous.
- J'ai vu les termes
Task
,Future
etCoroutine
utilisés dans la documentationasyncio
. Quelles sont les différences entre eux?
Ce sont des concepts très différents. Principalement, Task
se compose de Future
et Coroutine
. Décrivons brièvement ces primitives (je vais simplifier beaucoup de choses pour ne décrire que les grands principes):
Futur
L'avenir est simplement une abstraction de valeur qui peut ne pas encore être calculée et sera disponible à terme. C'est un simple conteneur qui ne fait qu'une seule chose - chaque fois que la valeur est définie, déclenchez tous les rappels enregistrés.
Si vous souhaitez obtenir cette valeur, vous enregistrez un rappel via la méthode add_done_callback()
.
Mais contrairement à Promise
, le calcul réel est effectué en externe - et ce code externe doit appeler la méthode set_result()
pour résoudre l'avenir.
Coroutine
Coroutine est l'objet très similaire à Generator
.
Un générateur est généralement itéré dans la boucle for
. Il donne valeurs et, à partir de PEP342 acceptation, il reçoit valeurs.
Une coroutine est généralement itérée dans la boucle d'événements dans les profondeurs de la bibliothèque asyncio
. Une coroutine renvoie Future
instances. Lorsque vous parcourez une coroutine et qu'elle donne un avenir, vous devez attendre que cet avenir soit résolu. Après cela, vous devez send
la valeur du futur dans la coroutine, puis vous recevez un autre futur, et ainsi de suite.
Une expression await
est pratiquement identique à l'expression yield from
, Donc par en attente autre coroutine, vous vous arrêtez jusqu'à ce que cette coroutine ait tout son avenir résolu, et obtenez le retour de la coroutine valeur. Le Future
est itérable en une seule fois et son itérateur renvoie le futur réel - cela signifie grosso modo que await future
Est égal à yield from future
Est égal à yield future
.
Tâche
La tâche est Future, qui a en fait commencé à calculer et est attachée à la boucle d'événements. C'est donc un type spécial de Future (la classe Task
est dérivée de la classe Future
), qui est associée à une boucle d'événement, et elle a une certaine coroutine, qui sert de Exécuteur de tâche.
La tâche est généralement créée par un objet de boucle d'événement: vous donnez une coroutine à la boucle, elle crée un objet de tâche et commence à itérer sur cette coroutine de la manière décrite ci-dessus. Une fois la coroutine terminée, l'avenir de la tâche est résolu par la valeur de retour de la coroutine.
Vous voyez, la tâche est assez similaire à JS Promise - elle encapsule le travail d'arrière-plan et son résultat.
Fonction Coroutine et fonction Async
Coroutine func est une usine de coroutines, comme la fonction de générateur pour les générateurs. Remarquez la différence entre la fonction coroutine de Python et la fonction asynchrone de Javascript - la fonction asynchrone JS, lorsqu'elle est appelée, crée une promesse et son générateur interne commence immédiatement à être itéré, tandis que la coroutine de Python ne fait rien, jusqu'à ce que la tâche soit créée dessus.
- Dois-je commencer à écrire Python code qui a toujours une boucle d'événement en cours d'exécution?
Si vous avez besoin d'une fonction asyncio, vous devriez. Il s'avère qu'il est assez difficile de mélanger du code synchrone et asynchrone - dans l'ensemble, votre programme devrait être asynchrone (mais vous pouvez lancer des blocs de code synchrone dans des threads séparés via l'API asyncio threadpool)
Ptyhon et ES6 attendent/async sont à peu près les mêmes, à quelques détails près.
Promise
Python: attendent/async sont basés sur Futures
. Les générateurs n'ont rien à voir avec cela.