Maison >interface Web >js tutoriel >Explication détaillée du code d'implémentation de l'exécution asynchrone de la fonction JavaScript
Supposons que vous ayez plusieurs fonctions fn1, fn2 et fn3 qui doivent être appelées dans l'ordre. La manière la plus simple est bien sûr :
fn1(); fn2(); fn3();<.> Mais parfois, ces fonctions sont ajoutées une par une pendant l'exécution, et vous ne savez pas quelles fonctions sont là lorsque vous les appelez. À ce stade, vous pouvez prédéfinir un tableau, y insérer les fonctions lors de l'ajout des fonctions. , et sélectionnez-les dans l'ordre dans le tableau si nécessaire, sortez-les un par un et appelez-les un par un :
var stack = []; // 执行其他操作,定义fn1 stack.push(fn1); // 执行其他操作,定义fn2、fn3 stack.push(fn2, fn3); // 调用的时候 stack.forEach(function(fn) { fn() });Ce n'est pas le cas. peu importe que la fonction ait un nom ou non. Vous pouvez également transmettre directement la fonction anonyme. Testons-le :
var stack = []; function fn1() { console.log('第一个调用'); } stack.push(fn1); function fn2() { console.log('第二个调用'); } stack.push(fn2, function() { console.log('第三个调用') }); stack.forEach(function(fn) { fn() }); // 按顺序输出'第一个调用'、'第二个调用'、'第三个调用'Cette implémentation fonctionne bien jusqu'à présent, mais nous avons ignoré une situation, qui est l'appel de fonctions asynchrones. L'asynchronie est un sujet incontournable en JavaScript. Je ne vais pas aborder ici les différents termes et concepts liés à l'asynchrone en JavaScript. Les lecteurs sont invités à le vérifier par eux-mêmes (comme un commentaire célèbre). Si vous savez que le code suivant affichera 1, 3 et 2, continuez à lire :
console.log(1); setTimeout(function() { console.log(2); }, 0); console.log(3);S'il y a une fonction dans la pile file d'attente : Notre implémentation de fonctions asynchrones similaires est foirée :
var stack = []; function fn1() { console.log('第一个调用') }; stack.push(fn1); function fn2() { setTimeout(function fn2Timeout() { console.log('第二个调用'); }, 0); } stack.push(fn2, function() { console.log('第三个调用') }); stack.forEach(function(fn) { fn() }); // 输出'第一个调用'、'第三个调用'、'第二个调用'Le problème est évident, fn2 est bien appelé dans l'ordre, mais la fonction fn2Timeout dans setTimeout () { console.log('Second call') } n'est pas exécuté immédiatement (même si le délai d'attente est défini sur 0) ; fn2 revient immédiatement après l'appel, puis exécute fn3. Après l'exécution de fn3, c'est vraiment le tour de fn2Timeout. .
function fn2() { setTimeout(function() { fn2Timeout(); fn3(); }, 0); }. Mais cela équivaut à supprimer le fn2Timeout d'origine et à le remplacer par une nouvelle fonction, puis à insérer les fn2Timeout et fn3 d'origine. Cette méthode de modification dynamique de la fonction d'origine porte un terme spécial appelé Monkey Patch. Selon le mantra de nos programmeurs : "Cela peut certainement être fait", mais c'est un peu délicat à écrire, et il est facile de s'impliquer. Existe-t-il une meilleure façon ?
Nous prenons du recul et n'insistons pas pour attendre que fn2Timeout soit complètement exécuté avant d'exécuter fn3. Au lieu de cela, nous l'appelons sur la dernière ligne du corps de la fonction fn2Timeout :
function fn2() { setTimeout(function fn2Timeout() { console.log('第二个调用'); fn3(); // 注{1} }, 0); }
Il y a un autre problème. Puisque fn3 doit être appelé dans fn2, nous ne pouvons pas appeler fn3 via stack.forEach, sinon fn3 sera appelé deux fois.
Nous ne pouvons pas écrire fn3 dans fn2. Au lieu de cela, il nous suffit de trouver la fonction suivante de fn2 dans la pile à la fin de fn2Timeout, puis d'appeler :
function fn2() { setTimeout(function fn2Timeout() { console.log('第二个调用'); next(); }, 0); }
var index = 0; function next() { var fn = stack[index]; index = index + 1; // 其实也可以用shift 把fn 拿出来 if (typeof fn === 'function') fn(); }next est utilisé comme ceci :
var stack = []; // 定义index 和next function fn1() { console.log('第一个调用'); next(); // stack 中每一个函数都必须调用`next` }; stack.push(fn1); function fn2() { setTimeout(function fn2Timeout() { console.log('第二个调用'); next(); // 调用`next` }, 0); } stack.push(fn2, function() { console.log('第三个调用'); next(); // 最后一个可以不调用,调用也没用。 }); next(); // 调用next,最终按顺序输出'第一个调用'、'第二个调用'、'第三个调用'。Chaque fonction doit appeler next. Si elle n'est pas écrite dans une certaine fonction, le programme se terminera directement après l'exécution de la fonction sans aucun mécanisme pour continuer.
Après avoir compris cette implémentation de la file d'attente des fonctions, vous devriez être capable de résoudre la question d'entretien suivante :
// 实现一个LazyMan,可以按照以下方式调用: LazyMan(“Hank”) /* 输出: Hi! This is Hank! */ LazyMan(“Hank”).sleep(10).eat(“dinner”)输出 /* 输出: Hi! This is Hank! // 等待10秒.. Wake up after 10 Eat dinner~ */ LazyMan(“Hank”).eat(“dinner”).eat(“supper”) /* 输出: Hi This is Hank! Eat dinner~ Eat supper~ */ LazyMan(“Hank”).sleepFirst(5).eat(“supper”) /* 等待5秒,输出 Wake up after 5 Hi This is Hank! Eat supper */ // 以此类推。
Si vous faites attention, vous verrez peut-être que ce next ne peut être placé qu'à la fin de la fonction pour le moment. S'il est placé au milieu, le problème d'origine se produira toujours :
function fn() { console.log(1); next(); console.log(2); // next()如果调用了异步函数,console.log(2)就会先执行 }
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!