Maison > Article > interface Web > Explication détaillée de la technologie de programmation asynchrone JavaScript
Mécanisme d'interrogation d'événements basé sur le navigateur (et le mécanisme d'interrogation d'événements dans Node.js), JavaScript s'exécute souvent dans un environnement asynchrone. En raison des caractéristiques du langage propre à JavaScript (il ne nécessite pas que les programmeurs contrôlent les threads/processus), il est très important de résoudre la programmation asynchrone en js. On peut dire que dans un projet complet, il est impossible pour les développeurs js de ne pas faire face à des opérations asynchrones. Cet article présentera en détail plusieurs méthodes classiques de sérialisation de programmation asynchrone JavaScript, et présentera également brièvement la méthode d'exécution séquentielle Promise fournie par ES6.
Supposons que nous ayons une méthode ajax()
, qui reçoit un paramètre d'URL, lancez une requête asynchrone à l'adresse, et exécutez le deuxième paramètre - une fonction de rappel à la fin de la requête :
ajax(url,function(result){ console.log(result); });
On peut dire que cette méthode est la méthode de fonction de rappel utilisée par presque tous les front-end développeur. , avec un tel mécanisme de rappel, les développeurs n'ont pas besoin d'écrire du code comme celui-ci pour deviner quand la requête du serveur reviendra :
var result=ajax(url); setTimeout(function(result){ console.log(result); },400);
Vous devriez être capable de comprendre ce que je veux exprimer ici. Nous définissons une minuterie avec un délai de 400 millisecondes, en supposant que la requête ajax que nous effectuons sera terminée dans les 400 millisecondes. Dans le cas contraire, nous opérerons sur un undefined
de result
.
Mais il y a un problème qui émerge au fur et à mesure que le projet s'étend : si la scène nous oblige à avoir plusieurs couches de fonctions de rappel imbriquées, le code deviendra difficile à lire et à maintenir :
ajax(url0,function(result0){ ajax(result0.url1,function(result1){ ajax(result1.url2,function(result2){ console.log(result2); }); }); });
Afin de résoudre le problème de confusion de code exposé par les fonctions de rappel en ligne, nous introduisons des appels de fonctions externes pour résoudre des problèmes similaires :
function handle2(result){ console.log(result); }function handle1(result){ ajax(result.url,function(result){ handle2(result); }); } ajax(url,function(result){ handle1(result); });
Grâce à cette division des fonctions en ligne , nous appelons La méthode d'optimisation des fonctions externes peut grandement maintenir la simplicité du code.
En observant les outils de contrôle de processus JavaScript populaires, tels que Nimble, Step et Seq, nous apprendrons un modèle de conception simple : le contrôle via un gestionnaire de rappel. Exécution JavaScript asynchrone flux, ce qui suit est un exemple de code clé d'un gestionnaire de rappel typique :
var Flow={};//设置next方法,在上一个方法完成时调用下一个方法Flow.next=function(){ if(this.stack[0]){ //弹出方法栈中的第一个方法,并执行他 this.stack.shift()(); } };//设置series方法,接收一个函数数组,并按序执行Flow.series=function(arr){ this.stack=arr; this.next(); };//通过Flow.series我们能够控制传入的函数的执行顺序Flow.series([ function(){ //do something console.log(1); Flow.next(); }, function(next){ //do something console.log(2); Flow.next(); } ]);
Nous avons initialisé un contrôleur Flow
et conçu deux series
et next
pour ses attributs de fonction. Dans la méthode métier que nous avons écrite, la méthode suivante est déclenchée séquentiellement en appelant Flow.next()
à la fin de la méthode ; la fonction asynchrone est exécutée séquentiellement en exécutant la méthode series
. Cette façon de gérer les appels de fonctions asynchrones via le contrôleur principal simplifie notre processus de programmation, permettant aux développeurs de consacrer plus d'énergie à la logique métier.
Peut-être que la méthode asynchrone introduite ci-dessus ne peut toujours pas répondre aux scénarios commerciaux en développement réel : supposons que nous ayons a()
, b()
, c()
trois méthodes, a et b n'ont aucune dépendance et peuvent être exécutées de manière asynchrone. Mais c ne peut être déclenché qu’une fois a et b terminés. Afin de répondre à une telle implémentation logique, nous ajoutons un compteur global pour contrôler le flux d'exécution du code :
var flag=2;var aValue,bValue;function a(){ aValue=1; flag--; c(); }function b(){ setTimeout(function(){ bValue=2; flag--; c(); },200); }function c(){ if(flag==0){ console.log("after a and b:"+(aValue+bValue)); } } a(); b();
Nous définissons un indicateur de variable globale pour surveiller l'achèvement de la méthode a et de la méthode b. La méthode b simule l'environnement réseau en définissant un minuteur de 200 millisecondes et appellera finalement la méthode c avec succès une fois la méthode b exécutée. De cette façon, nous implémentons des appels dépendants aux méthodes a()
, b()
et c()
.
Lorsque la solution ci-dessus est appliquée dans des scénarios complexes, les problèmes suivants se produiront : le produit a traversé plusieurs itérations de version et la méthode c s'appuie sur plus de méthodes, donc l'indicateur de compteur doit être constamment modifié ; les développeurs sont modifiés au cours du processus d'itération du produit. Lorsque les deux situations ci-dessus se produisent, la logique du code devient confuse. La question de savoir si la balise flag peut rester concise et correcte dépend largement des itérations du produit. Par conséquent, nous proposons des améliorations d’optimisation orientées données.
Dans les scénarios de développement réels, la raison de l'existence de dépendances de méthode est essentiellement due à l'existence de dépendances de données. Pour l'exemple simple ci-dessus : la méthode c dépend des résultats des opérations de la méthode a et de la méthode b, plutôt que de la méthode b. si le drapeau est 0. Par conséquent, nous pouvons remplacer la vérification si le marqueur a été mis à 0 par vérifier si la méthode dépendante a terminé le traitement des données. Dans cet exemple, nous vérifions si aValue et bValue ont terminé l'affectation dans la méthode c :
function c(){ if(aValue!==undefined && bValue!==undefined){ console.log("after a and b:"+(aValue+bValue)); } }<.>Pour un scénario plus général, nous modifions le code ci-dessus comme suit :
var checkDependency={};var aValue,bValue;function a(){ aValue=1; checkDependency.a=true; c(); }function b(){ setTimeout(function(){ bValue=2; checkDependency.b=true; c(); },200); }function c(){ if(checkDependency.a && checkDependency.b){ console.log("after a and b:"+(aValue+bValue)); } } a(); b();Avec la méthode d'inspection orientée données, lors de l'expansion future, il suffit d'ajouter des paires à la nouvelle méthode
Modifiez l'objet et vérifiez l'existence des attributs correspondants dans la méthode c pour réaliser l'exécution séquentielle des méthodes dépendantes asynchrones. checkDependency
var bool=false;/* * 新建一个Promise实例,向构造函数传入一个异步执行函数 * 异步函数会接受两个参数,由Promise传入,对应then方法中传入的方法 */var promise=new Promise(function(resolve,reject){ setTimeout(function(){ if(bool){ //根据执行情况相应调用resolve和reject resolve(bool); }else{ reject(bool); } },200); });//通过then向Promise实例传入解决方法promise.then(function resolve(result){ console.log("success"); },function reject(result){ console.log("failure"); });Le L'exemple de code ci-dessus montre une application Promise de base. Peut-être que l'appel de chaîne suivant est plus courant dans les scénarios réels :
new Promise(function(res,rej){ if(/*异步调用成功*/){ res(data); }else{ rej(error); } }).then(function resolve(result){ console.log("success"); },function reject(result){ console.log("failure"); });
如果对Promise感兴趣的话,可以在网上寻找资料继续深入学习!
关于Promise的兼容性,通常web前端JavaScript代码中不会直接使用Promise(通过caniuse.com网站查询发现Android4.4不支持Promise)。如果特别想使用的,往往会在项目中附带一些补足兼容性的promise类库;而后端Node.js可以放心使用Promise类来管理异步逻辑。
以上就是详解JavaScript异步编程技术的内容,更多相关内容请关注PHP中文网(www.php.cn)!