Dieser Artikel stellt hauptsächlich die relevanten Informationen zur JavaScript-Warteschlangenfunktion und zur asynchronen Ausführung vor. Er hat einen bestimmten Referenzwert.
Hinweis bearbeiten: Ich habe etwas Ähnliches gesehen Warteschlangenfunktion beim Überprüfen des JavaScript-Codes anderer Leute, aber ich habe es nicht ganz verstanden. Es stellt sich heraus, dass dies dazu dient, sicherzustellen, dass die Funktionen in der richtigen Reihenfolge aufgerufen werden. Nachdem ich diesen Artikel gelesen hatte, stellte ich fest, dass er auch für die asynchrone Ausführung usw. verwendet werden kann.
Angenommen, Sie haben mehrere Funktionen fn1, fn2 und fn3, die nacheinander aufgerufen werden müssen:
fn1(); fn2(); fn3();
Aber manchmal werden diese Funktionen zur Laufzeit einzeln hinzugefügt, und Sie wissen zu diesem Zeitpunkt nicht, welche Funktionen vorhanden sind. Sie können ein Array vordefinieren Schieben Sie die Funktion hinein, wenn Sie sie hinzufügen, nehmen Sie sie bei Bedarf nacheinander aus dem Array heraus und rufen Sie sie nacheinander auf:
var stack = []; // 执行其他操作,定义fn1 stack.push(fn1); // 执行其他操作,定义fn2、fn3 stack.push(fn2, fn3); // 调用的时候 stack.forEach(function(fn) { fn() });
Diese Funktion hat Es spielt keine Rolle, ob sie keinen Namen hat. Sie können die anonyme Funktion einfach direkt übergeben. Testen wir es:
var stack = []; function fn1() { console.log('第一个调用'); } stack.push(fn1); function fn2() { console.log('第二个调用'); } stack.push(fn2, function() { console.log('第三个调用') }); stack.forEach(function(fn) { fn() }); // 按顺序输出'第一个调用'、'第二个调用'、'第三个调用'
Diese Implementierung funktioniert bisher gut, aber wir haben eine Situation ignoriert, nämlich asynchroner Funktionsaufruf. Asynchronität ist ein unvermeidliches Thema in JavaScript. Ich werde hier nicht auf die verschiedenen Begriffe und Konzepte im Zusammenhang mit Asynchronität in JavaScript eingehen. Die Leser werden gebeten, sich selbst damit auseinanderzusetzen (z. B. einen berühmten Kommentar). Wenn Sie wissen, dass der folgende Code 1, 3 und 2 ausgibt, lesen Sie bitte weiter:
console.log(1); setTimeout(function() { console.log(2); }, 0); console.log(3);
Wenn es in der Stapelwarteschlange eine Funktion gibt, die einer ähnlichen asynchronen Funktion ist, wird unsere Implementierung durcheinander gebracht:
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() }); // 输出'第一个调用'、'第三个调用'、'第二个调用'
Das Problem liegt auf der Hand, fn2 wird zwar nacheinander aufgerufen, aber die Funktion fn2Timeout() { console.log('second call') } in setTimeout wird nicht sofort ausgeführt (auch wenn timeout danach auf 0 gesetzt ist); fn2 heißt sofort Return und führt dann fn3 aus. Nachdem fn3 ausgeführt wurde, ist fn2Timeout tatsächlich an der Reihe.
Wie löst man das Problem? Lassen Sie es uns analysieren. Der Schlüssel hier ist fn2Timeout. Wir müssen warten, bis er tatsächlich ausgeführt wird, bevor wir fn3 aufrufen
Dies zu tun ist jedoch gleichbedeutend mit dem Entfernen des ursprünglichen fn2Timeout und dem Ersetzen durch eine neue Funktion und dem anschließenden Einfügen des ursprünglichen fn2Timeout und fn3. Für diese Methode zur dynamischen Änderung der ursprünglichen Funktion gibt es einen speziellen Begriff namens Monkey Patch. Ganz nach dem Mantra unserer Programmierer: „Es ist auf jeden Fall machbar“, aber das Schreiben ist etwas umständlich und man kann sich leicht darauf einlassen. Gibt es einen besseren Weg?
Wir gehen einen Schritt zurück und bestehen nicht darauf, auf die vollständige Ausführung von fn2Timeout zu warten, bevor wir fn3 ausführen. Stattdessen rufen wir es in der letzten Zeile des fn2Timeout-Funktionskörpers auf:
function fn2() { setTimeout(function() { fn2Timeout(); fn3(); }, 0); }
Das sieht besser aus, aber als fn2 definiert wurde, gab es noch kein fn3. Woher kam fn3?
function fn2() { setTimeout(function fn2Timeout() { console.log('第二个调用'); fn3(); // 注{1} }, 0); }
Wir können fn3 nicht in fn2 schreiben. Stattdessen müssen wir nur die nächste Funktion von fn2 im Stapel am Ende von fn2Timeout finden und dann aufrufen:
Diese nächste Funktion ist dafür verantwortlich, die nächste Funktion im Stapel zu finden und auszuführen. Lassen Sie uns jetzt next implementieren:
function fn2() { setTimeout(function fn2Timeout() { console.log('第二个调用'); next(); }, 0); }
next verwendet stack[index], um die Funktionen im Stack abzurufen next Once erhöht den Index um 1 und erreicht damit den Zweck, die nächste Funktion herauszunehmen.
next wird wie folgt verwendet:
var index = 0; function next() { var fn = stack[index]; index = index + 1; // 其实也可以用shift 把fn 拿出来 if (typeof fn === 'function') fn(); }
Jetzt wurde die stack.forEach-Zeile gelöscht, wir Rufen Sie es selbst auf. Sobald next, next die erste Funktion fn1 im Stapel findet, die ausgeführt werden soll, ruft next in fn1 auf, um die nächste Funktion fn2 zu finden und auszuführen, ruft dann next in fn2 auf und so weiter.
Jede Funktion muss als nächstes aufgerufen werden. Wenn sie nicht in einer bestimmten Funktion geschrieben ist, wird das Programm direkt nach der Ausführung der Funktion beendet, ohne dass es einen Mechanismus zum Fortfahren gibt.
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,最终按顺序输出'第一个调用'、'第二个调用'、'第三个调用'。
Node.js
// 实现一个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 */ // 以此类推。
die
Middleware
Wenn Sie vorsichtig sind, können Sie feststellen, dass dieser nächste Schritt vorerst nur am Ende der Funktion platziert werden kann. Wenn er in der Mitte platziert wird, tritt das ursprüngliche Problem weiterhin auf:
Durch unterschiedliche Implementierungen können Redux und Koa nach der Ausführung in der Mitte der Funktion platziert werden Nachfolgende Funktion, es kann dann zurückgedreht werden, um als nächstes den folgenden Code auszuführen. Das ist sehr clever. Schreiben Sie noch einmal, wenn Sie Zeit haben.
function fn() { console.log(1); next(); console.log(2); // next()如果调用了异步函数,console.log(2)就会先执行 }
Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der JavaScript-Warteschlangenfunktionen und der asynchronen Ausführung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!