Heim  >  Artikel  >  Web-Frontend  >  Detaillierte Erläuterung der JavaScript-Warteschlangenfunktion und der asynchronen_Javascript-Fähigkeiten

Detaillierte Erläuterung der JavaScript-Warteschlangenfunktion und der asynchronen_Javascript-Fähigkeiten

微波
微波Original
2017-06-28 13:21:311305Durchsuche

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 nacheinander hinzugefügt Ja, Sie wissen zu diesem Zeitpunkt nicht, welche Funktionen vorhanden sind. Sie können ein Array vordefinieren, die Funktionen beim Hinzufügen von Funktionen hineinschieben und sie nacheinander aus dem Array herausnehmen benötigt, und rufen Sie sie nacheinander auf:

var stack = [];
// 执行其他操作,定义fn1
stack.push(fn1);
// 执行其他操作,定义fn2、fn3
stack.push(fn2, fn3);
// 调用的时候
stack.forEach(function(fn) { fn() });

Auf diese Weise spielt es keine Rolle, ob die Funktion einen Namen hat oder nicht. 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 den Aufruf asynchroner Funktionen. 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 so sein durcheinander:

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 ist offensichtlich, dass fn2 tatsächlich der Reihe nach aufgerufen wird, aber die Funktion fn2Timeout() { console.log('Second call') } in setTimeout wird nicht sofort ausgeführt (auch wenn timeout ist auf 0 gesetzt); fn2 kehrt sofort nach dem Aufruf zurück und führt dann fn3 aus. Nachdem fn3 ausgeführt wurde, ist fn2Timeout tatsächlich an der Reihe.

Wie löst man das Problem? Nach unserer Analyse ist der Schlüssel hier fn2Timeout. Wir müssen warten, bis er tatsächlich ausgeführt wird, bevor wir fn3 aufrufen . in eine neue Funktion und fügen Sie dann das Original fn2Timeout und fn3 ein. 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 treten einen Schritt zurück und bestehen nicht darauf, darauf zu warten, dass fn2Timeout vollständig ausgeführt wird, 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 aus besser, aber definieren Sie fn2. Damals gab es noch kein fn3. Woher kam dieses fn3?

function fn2() {
  setTimeout(function fn2Timeout() {
    console.log('第二个调用');
    fn3();    // 注{1}
  }, 0);
}
Es gibt ein weiteres Problem. Da fn3 in fn2 aufgerufen werden muss, können wir fn3 nicht über stack.forEach aufrufen, da sonst fn3 zweimal aufgerufen wird.

Wir können fn3 nicht in fn2 schreiben. Stattdessen müssen wir am Ende von fn2Timeout nur die nächste Funktion von fn2 im Stapel finden und dann Folgendes 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 Funktion im Stapel abzurufen. Bei jedem Aufruf von next wird der Index um 1 erhöht, wodurch der Zweck des Abrufens des nächsten erreicht wird Funktion.

Next wird wie folgt verwendet:
var index = 0;

function next() {
  var fn = stack[index];
  index = index + 1; // 其实也可以用shift 把fn 拿出来
  if (typeof fn === 'function') fn();
}


Nachdem die Zeile „stack.forEach“ gelöscht wurde, rufen wir „next“ selbst auf, suchen die erste auszuführende Funktion fn1 und rufen sie auf next in fn1, um die nächste Funktion fn2 zu finden und auszuführen, dann next in fn2 aufzurufen 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,最终按顺序输出'第一个调用'、'第二个调用'、'第三个调用'。

Nachdem Sie diese Implementierung der Funktionswarteschlange verstanden haben, sollten Sie in der Lage sein, die folgende Interviewfrage zu lösen:

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
*/

// 以此类推。
Zhongdas berühmte Verbindung

Dies So implementiert das Framework die Middleware-Warteschlange. Wenn Sie interessiert sind, können Sie sich den Quellcode oder diese Interpretation von „Was ist Connect-Middleware“ ansehen. 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:

redux and Durch verschiedene Implementierungen kann koa next in der Mitte der Funktion platzieren, nachdem es die nachfolgenden Funktionen ausgeführt hat, kann es dann zurückgehen und den folgenden Code als nächstes ausführen, was sehr clever ist. Schreiben Sie noch einmal, wenn Sie Zeit haben.

Das obige ist der detaillierte Inhalt vonDetaillierte Erläuterung der JavaScript-Warteschlangenfunktion und der asynchronen_Javascript-Fähigkeiten. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn