Résumé: Existe-t-il des modèles de bonnes pratiques bien établis que je peux suivre pour garder mon code lisible malgré l'utilisation de code asynchrone et de rappels?
J'utilise une bibliothèque JavaScript qui fait beaucoup de choses de manière asynchrone et s'appuie fortement sur les rappels. Il semble que l'écriture d'une méthode simple "charger A, charger B, ..." devienne assez compliquée et difficile à suivre en utilisant ce modèle.
Permettez-moi de donner un exemple (artificiel). Disons que je veux charger un tas d'images (de manière asynchrone) à partir d'un serveur Web distant. En C # / async, j'écrirais quelque chose comme ceci:
disableStartButton();
foreach (myData in myRepository) {
var result = await LoadImageAsync("http://my/server/GetImage?" + myData.Id);
if (result.Success) {
myData.Image = result.Data;
} else {
write("error loading Image " + myData.Id);
return;
}
}
write("success");
enableStartButton();
La disposition du code suit le "flux d'événements": tout d'abord, le bouton de démarrage est désactivé, puis les images sont chargées ( await
garantit que l'interface utilisateur reste sensible), puis le bouton de démarrage est réactivé.
En JavaScript, en utilisant des rappels, j'ai trouvé ceci:
disableStartButton();
var count = myRepository.length;
function loadImage(i) {
if (i >= count) {
write("success");
enableStartButton();
return;
}
myData = myRepository[i];
LoadImageAsync("http://my/server/GetImage?" + myData.Id,
function(success, data) {
if (success) {
myData.Image = data;
} else {
write("error loading image " + myData.Id);
return;
}
loadImage(i+1);
}
);
}
loadImage(0);
Je pense que les inconvénients sont évidents: j'ai dû retravailler la boucle en un appel récursif, le code qui devrait être exécuté à la fin est quelque part au milieu de la fonction, le code commençant le téléchargement ( loadImage(0)
) est tout en bas, et c'est généralement beaucoup plus difficile à lire et à suivre. C'est moche et je n'aime pas ça.
Je suis sûr que je ne suis pas le premier à rencontrer ce problème, donc ma question est: Y a-t-il des modèles de bonnes pratiques bien établis que je peux suivre pour garder mon code lisible malgré l'utilisation de code asynchrone et de rappels?
LoadImageAsync
c'est en fait un appel àExt.Ajax.request
Sencha Touch.async.waterfall
est votre réponse.Réponses:
Il est très peu probable que vous puissiez obtenir avec plain js le même niveau de concision et d'expressivité en travaillant avec les rappels que C # 5. Le compilateur fait le travail en écrivant tout ce passe-partout pour vous, et jusqu'à ce que les runtimes js le fassent, vous devrez toujours passer un rappel occasionnel ici et là.
Cependant, vous ne voudrez peut-être pas toujours ramener les rappels au niveau de simplicité du code linéaire - les fonctions de lancement ne doivent pas être laides, il y a un monde entier qui travaille avec ce type de code, et elles restent saines sans
async
etawait
.Par exemple, utilisez des fonctions d'ordre supérieur (mes js peuvent être un peu rouillés):
la source
Cela m'a pris un peu pour décoder pourquoi vous le faites de cette façon, mais je pense que cela pourrait être proche de ce que vous voulez?
Il commence d'abord par autant de requêtes AJAX qu'il peut le faire simultanément, et il attend que toutes soient terminées avant d'activer le bouton Démarrer. Ce sera plus rapide qu'une attente séquentielle et, je pense, est beaucoup plus facile à suivre.
EDIT : Notez que cela suppose que vous avez
.each()
disponible, et c'estmyRepository
un tableau. Faites attention à l'itération de boucle que vous utilisez ici à la place de cela, si elle n'est pas disponible - celle-ci tire parti des propriétés de fermeture pour le rappel. Je ne suis pas sûr de ce dont vous disposez, car ilLoadImageAsync
semble faire partie d'une bibliothèque spécialisée - je ne vois aucun résultat dans Google.la source
.each()
disposition, et, maintenant que vous le mentionnez, il n'est pas strictement nécessaire de faire le chargement séquentiellement. Je vais certainement essayer votre solution. (Bien que j'accepte la réponse de vski, car elle est plus proche de la question originale et plus générale.)Avertissement: cette réponse ne répond pas spécifiquement à votre problème, c'est une réponse générique à la question: "Y a-t-il des modèles de bonnes pratiques bien établis que je peux suivre pour garder mon code lisible malgré l'utilisation de code asynchrone et de rappels?"
D'après ce que je sais, il n'y a pas de modèle "bien établi" pour gérer cela. Cependant, j'ai vu deux types de méthodes utilisées pour éviter les cauchemars de rappels imbriqués.
1 / Utiliser des fonctions nommées au lieu de rappels anonymes
De cette façon, vous évitez l'imbrication en envoyant la logique de la fonction anonyme dans une autre fonction.
2 / Utilisation d'une bibliothèque de gestion de flux. J'aime utiliser Step , mais c'est juste une question de préférence. C'est celui que LinkedIn utilise d'ailleurs.
J'utilise une bibliothèque de gestion de flux lorsque j'utilise beaucoup de rappels imbriqués, car elle est beaucoup plus lisible lorsqu'il y a beaucoup de code l'utilisant.
la source
Autrement dit, JavaScript n'a pas le sucre syntaxique de
await
.Mais déplacer la partie "fin" dans le bas de la fonction est facile; et avec une fonction anonyme exécutant immédiatement, nous pouvons éviter de lui déclarer une référence.
Vous pouvez également passer la partie "fin" comme rappel de réussite à la fonction.
la source