Was sind zurückgestellte Rückrufe?

15

Ich verstehe die Idee eines Rückrufs, bei dem ich eine Funktion in eine andere Funktion übergebe und diese Funktion dann die bereitgestellte Funktion nach Belieben verwendet.

Ich habe Mühe, zurückgestellte Rückrufe zu verstehen, auch nachdem ich sie gegoogelt habe.

Kann mir bitte jemand eine einfache Erklärung geben? Ich programmiere in Ruby, kenne C / C ++ aber vor allem war ich ein erfahrener Assembler-Programmierer. Ich frage mich also, ist es ein bisschen wie ein Stapel von Rückrufadressen, die pop'd werden? Ich hoffe, jquery oder node.js zu lernen, und diese zurückgestellten Rückrufe scheinen für beide von wesentlicher Bedeutung zu sein. Ich verstehe die grundlegenden Prinzipien des Einfädelns (obwohl ein Mutex-Objekt meinen Kopf verletzt;)

zehn Mal
quelle
Meinen Sie die DeferredObjekte von jQuery ? Geht es um etwas Spezielles für Node.js?
Bfavaretto
1
Nein, ich meine im Allgemeinen. Obwohl ich jquery und möglicherweise node.js lernen möchte, hatte ich das Gefühl, dass ich einen Überblick darüber bekommen muss, was ein verzögerter Rückruf eigentlich zuerst ist. Ich habe den Wikipedia-Artikel über Rückrufe gelesen, aber ich konnte kein Verständnis für zurückgestellte Rückrufe gewinnen, die dem Paradigma der asynchronen Operation, die in diesen Sprachen, die sie verwenden, enthalten sein wird, eigen zu sein scheinen.
1
Ich bitte wirklich um die konzeptionelle Idee eines aufgeschobenen Rückrufs im Gegensatz zu deren Implementierung - sorry, wenn ich das nicht klarer formuliert hätte. Ich gab Sprachbeispiele mehr, um die Idee, die ich zu klären versuche, und auch meinen Programmierhintergrund zu erklären, damit die Leute wissen, wie man die Antwort vorschlägt. Vielen Dank für die bisherigen Antworten - ich komme dorthin!
Vorläufig
Ok, ich glaube, ich habe es jetzt, Leute, dank euch allen! Ich weiß aber nicht, wie ich die Antwort machen soll. Cameron erklärte das Konzept am einfachsten und das war es, was ich wirklich wollte, aber auch andere stimmten zu und fügten mein Wissen hinzu. Ich bin mir nicht sicher, wie ich die Antwort akzeptieren soll, da ich neu darin bin;)
vorläufig

Antworten:

4

Auf Anfrage werden hier Kommentare als Antwort präsentiert:


Ich bin mir nicht sicher, ob Sie die Tatsache völlig verstehen, dass Funktionen in JS erstklassige Objekte sind und daher erst nach ihrer Erstellung gespeichert werden können, wenn sie benötigt werden.

Angenommen, Sie möchten in eine Datei schreiben und anschließend eine Protokollmeldung ausdrucken. Sie rufen also die Funktion "write ()" (oder was auch immer) auf und übergeben ihr eine Funktion, die die Protokollnachricht ausgibt (dies ist die verzögerte Rückruffunktion). "write ()" speichert intern einen Verweis auf die angegebene Funktion, beginnt mit dem Schreiben in die Datei und richtet einen eigenen Rückruf ein, um zu erfahren, wann der Schreibvorgang abgeschlossen ist. Es kehrt dann zurück, bevor der Schreibvorgang abgeschlossen ist. Wenn dies der Fall ist, wird der interne Rückruf auf irgendeine Weise aufgerufen (dies ist die Aufgabe des zugrunde liegenden Frameworks - im Fall von node.js erfolgt dies mit einer Ereignisschleife), die dann Ihren Rückruf aufruft, der die Protokollnachricht druckt.

Der "zurückgestellte" Teil bedeutet einfach, dass Ihre Rückruffunktion nicht sofort aufgerufen wird. Aufrufen wird aufgeschoben , bis die entsprechende Zeit. Bei asynchronen Funktionen wie vielen in node.js wird der angegebene Rückruf im Allgemeinen aufgerufen, wenn die Operation abgeschlossen ist (oder ein Fehler auftritt).

Das meiste Zeug ist in node.js asynchron, aber im Browser mit zB jQuery ist das meiste Zeug tatsächlich synchron (außer natürlich für AJAX-Anfragen). Da erstklassige Funktionen in JavaScript so praktisch sind (insbesondere aufgrund der hervorragenden Schließungsunterstützung), werden Rückrufe auch überall im Browser verwendet, für synchrone Vorgänge werden sie jedoch nicht "zurückgestellt" (außer, wenn sie nicht sofort von aufgerufen werden) Sie, aber später durch die Funktion, die Sie aufrufen).

Die Tatsache, dass das zugrunde liegende System ereignisgesteuert ist, ist orthogonal zur Verwendung von verzögerten Rückrufen. Sie können sich eine (sehr langsame) Version von node.js vorstellen, die einen Thread für jede Operation gestartet und dann Ihren angegebenen Rückruf aufgerufen hat, als der Thread seine Arbeit beendet hat, ohne Ereignisse zu verwenden. Das ist natürlich ein schreckliches Modell, aber es zeigt meinen Standpunkt :-)

Cameron
quelle
8

Die Funktionsweise eines verzögerten Rückrufs ist jedes Mal, wenn Sie einen Rückruf hinzufügen, dieser Rückruf an ein Array weitergeleitet. Wenn dann die Methode .resolve()oder .resolveWith()für das zurückgestellte Objekt aufgerufen wird, ist alles.done() Rückrufe im Array in der angegebenen Reihenfolge ausgeführt.

Nun können wir uns ansehen, was ein verzögertes Objekt ist. Nehmen Sie den folgenden Ausschnitt als Beispiel.

var deferred = $.Deferred();
var promise = deferred.promise();

Was wir jetzt haben, ist ein zurückgestelltes Objekt und das Versprechungsobjekt des zurückgestellten Objekts. Das latente Objekt hat die gleichen Methoden wie das Versprechen Objekt, aber das Versprechen Objekt nur die Methoden hat .done(), .fail()und .always()die verwendet werden , Rückrufe für die jeweiligen auf das latente Objekt hinzuzufügen event. Das zurückgestellte Objekt hat jedoch vor allem .resolve()und vor allem mehrere andere Methoden .reject(). Wenn diese Methoden für das zurückgestellte Objekt aufgerufen werden, werden alle Rückrufe aufgerufen. .resolve()löst die .done()und .always()Rückrufe aus, während die .reject()Methode die .fail()und .always()Rückrufe aufruft .

Im Allgemeinen wird das zurückgestellte Objekt in einem privaten Bereich ausgeblendet, und das Versprechungsobjekt wird von der Funktion zurückgegeben, damit Rückrufe darauf platziert werden können. Das verzögerte Objekt wird später aufgelöst, z. B. nach Abschluss einer Ajax-Anforderung oder nach dem Laden eines Bildes, nach einem setTimeout usw. Es ist auch wichtig zu wissen, dass ein verzögertes Objekt nur einmal aufgelöst werden kann. Wenn es bereits gelöst ist, werden seine Rückrufe sofort aufgerufen.

Hier ist ein weiteres Beispiel, das ich benutze:

function loadImage(url) {
    var def = $.Deferred(),
        img = new Image();
    $(img).on("load error",function(e){
        if (e.type === "error") {
            def.reject(url);
        }
        else {
            def.resolve(url);
        }
    });
    img.src = url;
    // return the promise object so that callbacks can
    // be defined on the deferred object.
    return def.promise();
}
loadImage("foobar.jpg").done(function(){
    alert("The image is loaded!");
}).fail(function(){
    alert("The image failed to load!");
}).always(function(){
    alert("This is always called!");
});

Weitere Informationen zur $.Deferred()Methode und zu zurückgestellten Objekten von jQuery finden Sie unter http://api.jquery.com/category/deferred-object/.

user400654
quelle
Dies wird wahrscheinlich von unschätzbarem Wert sein, wenn ich mich mit dem Konzept eines aufgeschobenen Rückrufs befasst habe. Entschuldigung, aber ich verstehe immer noch nicht, was ein verzögerter Rückruf ist. Ich suche mehr die konzeptionelle Idee dahinter. Irgendwie nach Mihias Idee. Wenn ich das einmal richtig verstanden habe, kann ich vielleicht js verstehen.
Tentimes
3

Ich bin mir nicht sicher, aber ich glaube, ein verzögerter Rückruf bezieht sich auf einen asynchronen Rückruf.

Die beste Erklärung fand ich unter http://www.nodebeginner.org

Hey, wahrscheinlichExpensiveFunction (), bitte mach deine Sachen, aber ich, der einzelne Node.js-Thread, werde hier nicht warten, bis du fertig bist. Ich werde weiterhin die Codezeilen unter dir ausführen, also würdest du es bitte nehmen diese callbackFunction () hier und ruf sie an, wenn du mit deinen teuren Sachen fertig bist? Vielen Dank!"

In diesem Beispiel ist wahrscheinlichExpensiveFunction eine nicht blockierende (oder asynchrone) Funktion. Dies bedeutet, dass es nicht sofort ausgeführt wird, sondern in einer sogenannten Ereignisschleife abgelegt wird. Der Thread node.js setzt die Ausführung fort, entscheidet sich jedoch zu einem bestimmten Zeitpunkt dafür, etwas aus der Ereignisschleife auszuführen. Wenn die Ausführung von "probablyExpensiveFunction" abgeschlossen ist, wird sie aufgerufen. Wenn die Ausführung von "probablyExpensiveFunction" abgeschlossen ist, wird der als Parameter übergebene (zurückgestellte) Rückruf aufgerufen.

Als Beispiel für die wahrscheinlich teure Funktion können Sie fs.readFile nehmen

mihai
quelle
Vielen Dank. Aber wie wird die teure Funktion ihre Sache machen, wenn sie bereits zurückgekehrt ist und Sie wieder im Hauptthread der Ausführung sind? Ich verstehe das nicht. Wollen Sie damit sagen, dass die Funktion nach dem Ende in irgendeiner Weise bestehen bleibt?
Bearbeitet die Antwort, vielleicht ist es jetzt klarer.
Sehr hilfreich. Aber ... muss ich dieses Array gespeicherter Rückrufe verarbeiten? Was ich meine ist, was ist es, was diese Liste verarbeitet. Ist es zum Beispiel so, dass js diese Rückrufe im Hintergrund aufhebt, ohne dass Sie etwas dagegen tun müssen, oder ist es so, dass ein Ereignis node.js oder etwas anderes einen bestimmten Rückruf aufruft. Entschuldigung, ich bekomme ungefähr 70% von dem, was du
sagst
@tentimes - "Was verarbeitet diese Liste?" Das $ .Deferred () - Objekt verarbeitet die Liste. Wenn Sie .resolve()oder .reject()auf dem ursprünglichen zurückgestellten Objekt aufrufen , wird die Liste der Rückrufe aufgerufen.
User400654
2
@tentimes: Nach dem, was Sie sagen, sind Sie sich nicht sicher, ob Sie die Tatsache, dass Funktionen in JS erstklassige Objekte sind und daher bis zu ihrer Erstellung gespeichert werden können. Angenommen, Sie möchten in eine Datei schreiben und anschließend eine Protokollnachricht ausdrucken. Sie rufen also die Funktion "write" (oder was auch immer) auf und übergeben ihr eine Funktion, die die Protokollnachricht ausgibt. "write ()" speichert intern einen Verweis auf die angegebene Funktion, beginnt mit dem Schreiben in die Datei und richtet einen eigenen Rückruf ein, um zu erfahren, wann der Schreibvorgang abgeschlossen ist. Es kehrt dann zurück, bevor der Schreibvorgang abgeschlossen ist. Wenn dies der Fall ist, wird Ihre Funktion aufgerufen.
Cameron
3

JavaScript ist Single-Threaded, so dass Sie nicht in Threads denken können, um dies zu verstehen. Hier ein Beispiel für reguläre und asynchrone Rückrufe mit jQuery:

var regularCallback = function(evt) {
    alert("I'm a callback!")
}
var asyncCallback = function(data) {
    alert("I only run when an async operation finishes!")
}

// Bind the regular callback to a button's click event
$('#mybutton').on('click', regularCallback);

// Start an ajax request to the server. The request is asynchronous, so code
// below this line will execute immediately. The callback function
// will only be called when the request is complete.
$.get("http://google.com", asyncCallback);
bfavaretto
quelle
Jetzt bringt mich das irgendwohin, danke. Der asynchrone Server reagiert also auf ein Ereignis, das ich mit ajax eingerichtet habe - das führt nur dazu, dass das Problem behoben wird, und wenn das Ereignis eintritt, wird mein Rückruf aufgerufen? Ich glaube, ich verstehe es. Würde node.js / jquery mit jquery usnig zurückgestellten Objekten und einem komplizierten System der Interaktion mit ihnen etwas Ähnliches tun, um wirklich nur dasselbe zu tun, aber Methoden zu verwenden?
Tentimes
1
@tentimes, genau! Rückrufe werden normalerweise als Reaktion auf Ereignisse ausgeführt (da "Rückruf" jedoch kein js-Sprachkonstrukt ist, wird der Begriff manchmal in anderen Kontexten verwendet). Die aufgeschobenen Gegenstände (Versprechen), die Sie in Kevins Antwort sehen, sind im Grunde syntaktischer Zucker. Sie werden "aufgelöst" oder "zurückgewiesen", wenn ein (asynchrones) Ereignis ausgelöst wird, und rufen dann den entsprechenden Rückruf auf ("erledigt" oder "fehlgeschlagen", dann "immer"). Die Verwendung in jQuery kann den Code lesbarer machen und einige zusätzliche Tricks ermöglichen (z. B. das einfache Hinzufügen eines zweiten Rückrufs nach dem Auslösen einer Ajax-Anforderung).
Bfavaretto
Beide sind asynchrone Rückrufe
Raynos
1

Mit verzögerten Rückrufen (auch Promices genannt ) können Sie sequenziellen asynchronen Code schreiben, ohne Schmerzen und Rückruf-Spaghetti:

$.when( doAjax(), doAnotherAjax() ).then( haveFunWithMoreAjax ).then( animateStuff );

'when' lässt Sie warten, bis Funktionen parallel zurückkehren, und thenkann nacheinander verkettet werden.

Eine Anmerkung: jQuery verzögert ! = Promices / A , ihre Syntax ist etwas anders.

Es gibt gute Artikel zum Thema: einen im IEBlog und einen in einem zufälligen Blog , ein Buch und eine beliebte Stackoverflow-Frage

c69
quelle
1
ähm ... es stellt sich heraus, dass OP etwas anderes gefragt hat ... nun, hoffen wir , dass diese Antwort vielleicht eines Tages jemand anderem hilft.
c69