Actuellement, un grand nombre d'opérations asynchrones sont impliquées dans la demande, et les pages réelles sont de plus en plus enclines à des applications à une page. À l'avenir, vous pouvez utiliser l'épine dorsale, angulaire, knockout et d'autres cadres, mais le problème de la programmation asynchrone est le premier problème à être rencontré. Avec la montée des nœuds, la programmation asynchrone est devenue un sujet très chaud. Après une période d'apprentissage et de pratique, certains détails de la programmation asynchrone sont résumés.
1. Classification de la programmation asynchrone
Les méthodes pour résoudre le problème asynchrone incluent généralement: rappel direct, mode pub / sous (mode événement), bibliothèque de contrôle de la bibliothèque asynchrone (comme asynchrone, quand), promesse, générateur, etc.
1.1 Fonction de rappel
Les fonctions de rappel sont couramment utilisées pour résoudre des solutions asynchrones, souvent exposées et utilisées, faciles à comprendre et très faciles à implémenter dans les bibliothèques ou les fonctions. Il s'agit également d'une méthode que les gens utilisent souvent lors de l'utilisation de la programmation asynchrone.
Cependant, la méthode de la fonction de rappel a les problèmes suivants:
1. Une pyramide imbriquée du mal peut être formée, et le code n'est pas facile à lire;
2. Une seule fonction de rappel peut être correspondante, ce qui devient une limite dans de nombreux scénarios.
1.2 Pub / sous-Mode (événement)
Ce modèle est également appelé mode événement, qui est l'événement des fonctions de rappel et est très courant dans les bibliothèques de classe telles que jQuery.
Le mode d'abonné de l'événement lui-même n'a pas le problème des appels synchrones et asynchrones, mais dans le nœud, les appels émit sont principalement déclenchés de manière asynchrone avec la boucle d'événement. Ce mode est souvent utilisé pour découpler la logique commerciale. Les éditeurs d'événements n'ont pas besoin de prêter attention à la fonction de rappel enregistrée, et ils n'ont pas besoin de prêter attention au nombre de fonctions de rappel. Les données peuvent être transmises de manière flexible via des messages.
Les avantages de ce modèle sont: 1. Facile à comprendre; 2. Pas plus limité à une fonction de rappel.
En ce qui concerne les mauvaises choses: 1. Vous devez utiliser la bibliothèque de classe; 2. L'ordre des événements et des fonctions de rappel est très important
La copie de code est la suivante:
var img = document.QuerySelect (#id);
img.addeventListener ('Load', function () {
// L'image est chargée
......
});
img.addeventListener ('error', function () {
// quelque chose s'est mal passé
......
});
Il y a deux problèmes avec le code ci-dessus:
un. IMG a en fait été chargé et la fonction de rappel de chargement est liée à l'heure actuelle. En conséquence, le rappel ne sera pas exécuté, mais il espère toujours exécuter la fonction de rappel correspondante.
La copie de code est la suivante:
var img = document.QuerySelect (#id);
Fonction Load () {
...
}
if (img.comptete) {
charger();
} autre {
img.addeventListener («charge», charge);
}
img.addeventListener ('error', function () {
// quelque chose s'est mal passé
......
});
né Impossible de bien gérer les exceptions
Conclusion: Le mécanisme de l'événement est le mieux adapté pour traiter les événements répétés sur le même objet, et il n'est pas nécessaire de considérer l'événement avant que la fonction de rappel ne soit liée.
1.3 Bibliothèque de contrôle asynchrone
Les bibliothèques asynchrones actuelles incluent principalement Q, When.js, Win.js, RSVP.js, etc.
La caractéristique de ces bibliothèques est que le code est linéaire et peut être écrit de haut en bas, conformément aux habitudes naturelles.
Les mauvaises choses sont également différentes dans les styles, qui ne sont pas pratique pour lire et augmenter les coûts d'apprentissage.
1.4 Promesse
La promesse est traduite en chinois comme promesse. Personnellement, après l'achèvement de manière asynchrone, il donnera un résultat externe (succès ou échec) et promettra que le résultat ne changera plus. En d'autres termes, la promesse reflète la valeur finale du résultat de rendement d'une opération (une promesse représente la valeur éventuelle renvoyée de l'achèvement unique d'une opération). À l'heure actuelle, la promesse a été introduite dans la spécification ES6, et des navigateurs avancés tels que Chrome et Firefox ont implémenté cette méthode native en interne, ce qui est assez pratique à utiliser.
Voici les caractéristiques de la promesse des aspects suivants:
1.4.1 Statut
Il contient trois états: en attente, rempli et rejeté. Les trois états ne peuvent subir que deux transitions (de l'attente ---> épanouie, en attente-> rejetée), et la transition de l'état ne peut se produire qu'une seule fois.
1.4.2 puis méthode
La méthode alors est utilisée pour spécifier la fonction de rappel une fois l'événement asynchrone terminé.
Cette méthode peut être considérée comme la méthode de la promesse de l'âme, qui est prometteuse pleine de magie. Il existe plusieurs manifestations spécifiques comme suit:
a) puis la méthode renvoie la promesse. Cela permet des opérations en série de plusieurs opérations asynchrones.
En ce qui concerne le cercle jaune 1 dans la figure ci-dessus, le traitement de la valeur est une partie plus compliquée de la promesse. Le traitement de la valeur est divisé en deux situations: promettre un objet et un objet non dans le cadre.
Lorsque la valeur n'est pas un type de promesse, utilisez simplement la valeur comme valeur du paramètre de la résolution de la deuxième promesse; Lorsqu'il s'agit d'un type de promesse, le statut et les paramètres de Promise2 sont complètement déterminés par la valeur. On peut considérer que Promsie2 est une marionnette de valeur, et Promise2 n'est qu'un pont reliant différents asynchrones.
La copie de code est la suivante:
Promesse.prototype.phen = fonction (onfulllad, onrejected) {
renvoyer une nouvelle promesse (fonction (résoudre, rejeter) {// La promesse ici est marquée comme promesse2
poignée({
ONFULLIED: onfullable,
sur le point de vue: sur le point de vue,
Résoudre: résoudre,
rejeter: rejeter
})
});
}
Poignée de fonction (différée) {
var handlefn;
if (state === 'épanoui') {
handlefn = report.onfulfillé;
} else if (state === 'rejeté') {
handlefn = Deferred.onreject;
}
var ret = handlefn (valeur);
Reforred.resolve (ret); // Notez que la résolution à l'heure actuelle est la résolution de promesse2
}
fonction Résolve (val) {
if (val && typeof val.then === 'function') {
val.phe (résolution); // Si Val est un objet de promesse ou un objet de promesse de classe, l'état de promesse2 est complètement déterminé par Val
retour;
}
if (callback) {// Le rappel est la fonction de rappel spécifiée
rappel (Val);
}
}
b) La conversion entre plusieurs bibliothèques asynchrones différentes est implémentée.
Il y a un objet appelé Thenable dans Asynchronous, qui fait référence à un objet avec la méthode alors. Tant qu'un objet objet a la méthode alors, il peut être converti, par exemple:
La copie de code est la suivante:
var deferred = $ ('aa.ajax'); // !! différé.Then === true
var p = promesse.resolve (différé);
P. Then (......)
1.4.3 CommonJS Promise / une spécification
Actuellement, il existe des spécifications Promise / A et Promise / A + pour les spécifications concernant la promesse, ce qui montre que la mise en œuvre de la promesse est assez compliquée.
La copie de code est la suivante:
Puis (FAILLIELHHANDLER, REJECTEDHANDLER, PROGRESSHANDLER)
1.4.4 Notes
La fonction de rappel dans une promesse partage la valeur. Dans le traitement des résultats, la valeur est transmise sous forme de paramètre à la fonction de rappel correspondante. Si la valeur est un objet, veillez à ne pas modifier facilement la valeur.
La copie de code est la suivante:
var p = promesse.resolve ({x: 1});
P. THEL (FONCTION (Val) {
console.log ('premier rappel:' + val.x ++);
});
P. THEL (FONCTION (Val) {
console.log ('Deuxième rappel:' + val.x)
})
// Premier rappel: 1
// Deuxième rappel: 2
1,5 générateur
Toutes les méthodes ci-dessus sont basées sur la fonction de rappel pour compléter les opérations asynchrones, et elles ne sont rien de plus que de résumer la fonction de rappel. ES6 propose un générateur, qui ajoute des moyens de résoudre les opérations asynchrones et n'est plus terminée en fonction des fonctions de rappel.
La plus grande caractéristique du générateur est qu'il peut faire une pause et redémarrer les fonctions, ce qui est très propice à la résolution d'opérations asynchrones. La combinaison de la pause du générateur avec la manipulation des exceptions de Promise peut résoudre le problème de programmation asynchrone plus élégamment. Référence de mise en œuvre spécifique: Kyle Simpson
2. Problèmes de programmation asynchrone
2.1 Gestion des exceptions
a) Les événements asynchrones incluent deux liens: émettre des demandes asynchrones et le traitement des résultats. Ces deux liens sont connectés via des boucles d'événements. Ensuite, lorsque vous essayez d'effectuer une capture d'exception, vous devez le capturer.
La copie de code est la suivante:
essayer {
asycevent (rappel);
} catch (err) {
......
}
Le code ci-dessus ne peut pas prendre l'exception dans le rappel et ne peut obtenir que l'exception dans le processus de demande. Cela a des problèmes: si l'émission de demande et le traitement de la demande sont complétées par deux personnes, alors il y a des problèmes lors du traitement des exceptions?
b) Promise Implémentez la livraison des exceptions, qui apporte certains avantages pour s'assurer que le code n'est pas bloqué dans les projets réels. Cependant, s'il existe de nombreux événements asynchrones, il n'est pas facile de savoir quel événement asynchrone produit une exception.
La copie de code est la suivante:
// Description de la scène: Afficher les informations d'alarme des prix dans CRM, y compris les informations compétitives. Cependant, il faut beaucoup de temps pour obtenir les informations compétitives. Afin d'éviter la requête lente, le backend divise un enregistrement en deux pièces pour l'obtenir séparément.
// Étape 1: obtenir des informations d'alarme de prix, en plus des informations de concurrence
fonction getPriceArmData () {
return nouvelle promesse (fonction (résolution) {
Y.io (url, {
Méthode: «Get»,
Données: paramètres,
on: function () {
Succès: fonction (id, données) {
Résolve (AlarmData);
}
}
});
});
}
// Après avoir obtenu les informations d'alarme, allez pour obtenir les informations de concours
getPriceArmData (). puis (fonction (data) {
// Rendu des données, en plus des informations compétitives
rendu (données);
return nouvelle promesse (fonction (résolution) {
Y.io (url, {
Méthode: «Get»,
Données: {AlarmList: Data},
on: function () {
Succès: fonction (id, compdata) {
résolution (compdata);
}
}
});
});
}) // Après avoir obtenu toutes les données, le rendu des informations de concurrence
.Then (fonction (data) {
// Rendez les informations sur les enchères
rendre (données)
}, fonction (err) {
// Gestion des exceptions
console.log (err);
});
Le code ci-dessus peut être converti à ce qui suit:
La copie de code est la suivante:
essayer{
// obtenir des informations d'alarme autres que la concurrence
var AlarmData = AlarmDataExceptCompare ();
rendu (alarmdata);
// Enquête sur les informations sur la concurrence basées sur les informations d'alarme
var compaTaTa = getCompareInfo (AlarmData);
rendu (comparéaTa);
} catch (err) {
Console.log (err.Message);
}
Dans l'exemple ci-dessus, l'exception est gérée à la fin, de sorte que lorsqu'une exception se produit dans un certain lien, nous ne pouvons pas savoir exactement quel événement est généré.
2.2 JQUERY.
Les opérations asynchrones sont également mises en œuvre dans jQuery, mais elles ne respectent pas les spécifications Promise / A + dans la mise en œuvre, et se reflètent principalement dans les aspects suivants:
un. Nombre de paramètres: la promesse standard ne peut accepter qu'un seul paramètre, tandis que jQuery peut transmettre plusieurs paramètres
La copie de code est la suivante:
fonction asyncinjquery () {
var d = new $ .deferred ();
setTimeout (function () {
D. Resolve (1, 2);
}, 100);
retour d.promise ()
}
asyncinjquery (). puis (fonction (val1, val2) {
Console.log ('Output:', Val1, Val2);
});
// Sortie: 1 2
né Gestion des exceptions dans le traitement des résultats
La copie de code est la suivante:
fonction asyncinpromise () {
return nouvelle promesse (fonction (résolution) {
setTimeout (function () {
var jsonstr = '{"name": "mt}';
résoudre (jsonstr);
}, 100);
});
}
asyncinpromise (). alors (fonction (val) {
var d = JSON.Parse (Val);
Console.log (D.Name);
}). alors (null, fonction (err) {
Console.log ('Show Error:' + err.Message);
});
// Afficher l'erreur: fin inattendue de l'entrée
fonction asyncinjquery () {
var d = new $ .deferred ();
setTimeout (function () {
var jsonstr = '{"name": "mt}';
D. Resolve (JSontr);
}, 100);
retour d.promise ()
}
asyncinjquery (). alors (fonction (val) {
var d = JSON.Parse (Val);
Console.log (D.Name);
}). puis (fonction (v) {
Console.log ('Success:', v.name);
}, fonction (err) {
Console.log ('Show Error:' + err.Message);
});
// Syntaxerror non cinglé: fin inattendue de l'entrée
On peut voir à partir de cela que la promesse traite le résultat de la fonction de rappel, qui peut capturer des exceptions lors de l'exécution de la fonction de rappel, mais JQuery.Deferred ne peut pas.