Heim >Web-Frontend >js-Tutorial >Detaillierte Erläuterung der JavaScript-Warteschlangenfunktionen und der asynchronen Ausführung

Detaillierte Erläuterung der JavaScript-Warteschlangenfunktionen und der asynchronen Ausführung

小云云
小云云Original
2017-12-18 10:57:012029Durchsuche

Ich habe eine ähnliche Warteschlangenfunktion gesehen, als ich auf den JavaScript-Code anderer Leute verwiesen habe, aber ich habe sie nicht verstanden. Es stellt sich heraus, dass dies dazu dient, sicherzustellen, dass die Funktionen in der richtigen Reihenfolge aufgerufen werden. Dieser Artikel stellt Ihnen hauptsächlich JavaScript-Warteschlangenfunktionen und asynchrone Ausführungsmethoden vor. Ich hoffe, er kann Ihnen helfen.

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 = [];// 执行其他操作,定义fn1stack.push(fn1);// 执行其他操作,定义fn2、fn3stack.push(fn2, fn3);// 调用的时候stack.forEach(function(fn) { fn() });

Es spielt 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 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 ist offensichtlich, fn2 wird zwar der Reihe nach aufgerufen, 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 kann man es lösen? 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 Originalfunktion 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 wird wie folgt verwendet:

var index = 0;

function next() {
    var fn = stack[index];    index = index + 1; // 其实也可以用shift 把fn 拿出来    if (typeof fn === 'function') fn();
}next通过stack[index]去获取stack中的函数,每调用next一次index会加1,从而达到取出下一个函数的目的。

Next muss in jeder Funktion aufgerufen werden, wenn es nicht in einer Funktion geschrieben ist, nachdem die Funktion ausgeführt wurde Das Programm wird direkt beendet, ohne dass es einen Mechanismus zum Fortsetzen gibt.

var stack = [];

// 定义index 和nextfunction 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,最终按顺序输出'第一个调用'、'第二个调用'、'第三个调用'。
现在stack.forEach一行已经删掉了,我们自行调用一次next,next会找出stack中的第一个函数fn1执行,fn1 里调用next,去找出下一个函数fn2并执行,fn2里再调用next,依此类推。
Nachdem Sie die Implementierung der Funktionswarteschlange verstanden haben, sollten Sie in der Lage sein, die folgende Interviewfrage zu lösen:

Wenn Sie vorsichtig sind, werden Sie möglicherweise feststellen, dass diese als nächstes nur platziert werden kann Wenn die Funktion vorerst in der Mitte platziert wird, erscheint immer noch das ursprüngliche Problem:

// 实现一个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
*/// 以此类推。
Node.js 中大名鼎鼎的connect框架正是这样实现中间件队列的。有兴趣可以去看看它的源码或者这篇解读《何为 connect 中间件》。

Redux und koa können durch unterschiedliche Implementierungen als nächstes in die Mitte gestellt werden Führen Sie nach der Funktion die folgenden Funktionen aus und kehren Sie dann zur nächsten Ausführung zurück. Der folgende Code ist sehr clever. Schreiben Sie noch einmal, wenn Sie Zeit haben.

function fn() {
    console.log(1);
    next();
    console.log(2); // next()如果调用了异步函数,console.log(2)就会先执行}
Verwandte Empfehlungen:

Teilen Sie einen gekapselten Javascript-Ereigniswarteschlangen-Funktionscode, um das Problem der Bindungsereignisse zu lösen

Ihr eigenes gekapseltes Javascript-Ereignis Warteschlangenfunktionsversion_Javascript-Kenntnisse

Jquery-Warteschlangenfunktionsverwendungsbeispiel_jquery

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!

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