Was sind zurückgestellte Objekte?

121

jQuery 1.5 fügt "Zurückgestellte Objekte" hinzu. Was sind sie und was genau machen sie?

Mohammad Ali Akbari
quelle

Antworten:

101

Zurückgestelltes Objekt

Ab jQuery 1.5 bietet das Objekt "Zurückgestellt" die Möglichkeit, mehrere Rückrufe in selbstverwalteten Rückrufwarteschlangen zu registrieren, Rückrufwarteschlangen entsprechend aufzurufen und den Erfolgs- oder Fehlerstatus einer synchronen oder asynchronen Funktion weiterzuleiten.

Aufgeschobene Methoden:

  • deferred.done ()
    • Fügen Sie Handler hinzu, die aufgerufen werden sollen, wenn das verzögerte Objekt aufgelöst wird.
  • deferred.fail ()
    • Fügen Sie Handler hinzu, die aufgerufen werden sollen, wenn das zurückgestellte Objekt abgelehnt wird.
  • deferred.isRejected ()
    • Stellen Sie fest, ob ein zurückgestelltes Objekt abgelehnt wurde.
  • deferred.isResolved ()
    • Stellen Sie fest, ob ein zurückgestelltes Objekt aufgelöst wurde.
  • deferred.reject ()
    • Lehnen Sie ein zurückgestelltes Objekt ab und rufen Sie alle failCallbacks mit den angegebenen Argumenten auf.
  • deferred.rejectWith ()
    • Lehnen Sie ein zurückgestelltes Objekt ab und rufen Sie alle failCallbacks mit dem angegebenen Kontext und den angegebenen Argumenten auf.
  • deferred.resolve ()
    • Lösen Sie ein verzögertes Objekt auf und rufen Sie alle doneCallbacks mit den angegebenen Argumenten auf.
  • deferred.resolveWith ()
    • Lösen Sie ein zurückgestelltes Objekt auf und rufen Sie doneCallbacks mit dem angegebenen Kontext und den angegebenen Argumenten auf.
  • deferred.then ()
    • Fügen Sie Handler hinzu, die aufgerufen werden sollen, wenn das zurückgestellte Objekt aufgelöst oder abgelehnt wird.

In Aktion zurückgestellt:

$.get("test.php").done(
    function(){ alert("$.get succeeded"); }
);

$.get("test.php")
    .done(function(){ alert("$.get succeeded"); })
    .fail(function(){ alert("$.get failed!"); });

Und es scheint, dass die vorhandenen Rückrufe der ajax () -Methode verkettet und nicht in den Einstellungen deklariert werden können:

var jqxhr = $.ajax({ url: "example.php" })
    .success(function() { alert("success"); })
    .error(function() { alert("error"); })
    .complete(function() { alert("complete"); });

Arbeitsbeispiel Aus dem Blog-Beitrag von Eric Hynds : http://jsfiddle.net/ehynds/Mrqf8/


jqXHR

Ab jQuery 1.5 gibt die Methode $ .ajax () das jXHR-Objekt zurück, das eine Obermenge des XMLHTTPRequest-Objekts ist. Weitere Informationen finden Sie im Abschnitt jXHR des Eintrags $ .ajax


Ab JQUERY 1.5 FREIGEGEBEN :

VERZÖGERTE OBJEKTE

Mit dem Umschreiben des Ajax-Moduls wurde eine neue Funktion eingeführt, die ebenfalls öffentlich zugänglich gemacht wurde: Zurückgestellte Objekte . Mit dieser API können Sie mit Rückgabewerten arbeiten, die möglicherweise nicht sofort vorhanden sind (z. B. das Rückgabeergebnis einer asynchronen Ajax-Anforderung). Darüber hinaus können Sie mehrere Ereignishandler anhängen (was in der Ajax-API bisher nicht möglich war).

Darüber hinaus können Sie mit der exponierten jQuery.Deferred Ihre eigenen zurückgestellten Objekte erstellen. Weitere Informationen zu dieser API finden Sie in der Dokumentation zu verzögerten Objekten .

Eric Hynds hat ein gutes Tutorial zur Verwendung von Deferreds in jQuery 1.5 geschrieben .

Jäger
quelle
19
Bitte erklären Sie mehr. Wie erstelle ich meine eigenen benutzerdefinierten verzögerten Objekte? Wie arbeiten Sie?
user113716
3
Eigentlich meine ich es ernst. Dies ist eine gute Frage zu einer brandneuen Funktion. Ich habe keine Ahnung, wie sie funktionieren, und ich denke, es wäre gut, wenn StackOverflow diese Frage für diejenigen gut erklärt hätte, die in Zukunft danach fragen werden.
user113716
1
Updates: Ich denke, die Definition von "Zurückgestellt", die ich oben hinzugefügt habe, gibt einen klareren Überblick darüber, was es tatsächlich tut. Es scheint mehr darum zu gehen, Rückrufe verketten zu können, als sie in Einstellungen deklarieren zu müssen, die an eine Funktion übergeben werden.
Jäger
1
@ Hunter Ich hätte auch gerne eine Erklärung, wie es funktioniert. Dies ist die erste Frage, also machen Sie es zu einer guten Antwort!
Raynos
2
Es gibt einige wichtige Vorteile: Sie können das Ergebnis einer möglichen asynchronen Aufgabe abstrahieren, mehrere Handler unterschiedlichen Typs binden, Handler an eine Aufgabe binden, auch nachdem die Aufgabe gelöst wurde, und das Ergebnis mehrerer asynchroner Anforderungen verknüpfen
Fügen Sie
13

Anstatt dir zu sagen, was es tut, zeige ich dir, was es tut und erkläre es.

Eine Kopie der zugehörigen Quelle von jQuery 1.5 mit Anmerkungen, die erklären, was es tut. Ich denke, die Kommentare sind größtenteils richtig.

Dies kann von Vorteil sein

// promiseMethods. These are the methods you get when you ask for a promise.
// A promise is a "read-only" version
// fullMethods = "then done fail resolve resolveWith reject rejectWith isResolve    isRejected promise cancel".split(" ")
// As you can see it removes resolve/reject so you can't actaully trigger a
// anything on the deferred object, only process callbacks when it "finishes".
promiseMethods = "then done fail isResolved isRejected promise".split(" "),

// Create a simple deferred (one callbacks list)
/* Class: _Deferred.
 *  methods: done, resolve, resolveWith, isResolved
 *  internal method: cancel
 *
 *  Basically allows you to attach callbacks with the done method.
 *  Then resolve the deferred action whenever you want with an argument.
 *  All the callbacks added with done will be called with the resolved argument
 *  Any callbacks attached after resolvement will fire immediatly.
 *
 *  resolveWith allows you to set the this scope in the callbacks fired.
 *
 *  isResolved just checks whether it's resolved yet.
 *
 *  cancel blocks resolve/resolveWith from firing. the methods added throug
 *  done will never be called
 */
_Deferred: function () {
    var // callbacks list
    callbacks = [],
        // stored [ context , args ]
        // stores the context & args that .resolve was called with
        fired,
        // to avoid firing when already doing so
        firing,
        // flag to know if the deferred has been cancelled
        // in Deferred cancel gets called after the first resolve call
        cancelled,
        // the deferred itself
        deferred = {

            // done( f1, f2, ...)
            done: function () {
                if (!cancelled) {
                    var args = arguments,
                        i, length,
                        // elem in callback list
                        elem,
                        // type of elem in callback list
                        type,
                        // cached context & args for when done is called
                        // after resolve has been
                        _fired;
                    // If resolve has been called already
                    if (fired) {
                        // mark it locally
                        _fired = fired;
                        // set fired to 0. This is neccesary to handle
                        // how done deals with arrays recursively
                        // only the original .done call handles fired
                        // any that unwrap arrays and call recursively
                        // dont handle the fired.
                        fired = 0;
                    }
                    // for each function append it to the callback list
                    for (i = 0, length = args.length; i < length; i++) {
                        elem = args[i];
                        type = jQuery.type(elem);
                        // if argument is an array then call done recursively
                        // effectively unwraps the array
                        if (type === "array") {
                            // def.done([f1, f2, f3]) goes to
                            // def.done(f1, f2, f3) through the apply
                            deferred.done.apply(deferred, elem);
                        } else if (type === "function") {
                            // if its a function add it to the callbacks
                            callbacks.push(elem);
                        }
                    }
                    // if it's already been resolved then call resolveWith using
                    // the cahced context and arguments to call the callbacks
                    // immediatly
                    if (_fired) {
                        deferred.resolveWith(_fired[0], _fired[1]);
                    }
                }
                return this;
            },

            // resolve with given context and args
            resolveWith: function (context, args) {
                                // if its been cancelled then we can't resolve
                                // if it has fired then we can't fire again
                                // if it's currently firing then we can't fire. This check is
                // there because of the try finally block. It ensures we
                // cant call resolve between the try & finally in the catch phase.
                if (!cancelled && !fired && !firing) {
                    firing = 1;
                    // try block because your calling external callbacks
                    // made by the user which are not bugfree.
                                        // the finally block will always run no matter how bad
                                        // the internal code is.
                    try {
                        while (callbacks[0]) {
                            callbacks.shift().apply(context, args);
                        }
                                        // cache the content and arguments taht have been called
                                        // and set firing to false.
                    } finally {
                        fired = [context, args];
                        firing = 0;
                    }
                }
                return this;
            },

            // resolve with this as context and given arguments
            // just maps to resolveWith, this sets the this scope as normal
            // maps to this.promise which is the read only version of Deferred.
            resolve: function () {
                deferred.resolveWith(jQuery.isFunction(this.promise) ? this.promise() : 
this, arguments);
                return this;
            },

            // Has this deferred been resolved?
            // checks whether it's firing or if it has fired.
            isResolved: function () {
                return !!(firing || fired);
            },

            // Cancels the action. To be used internally
            cancel: function () {
                cancelled = 1;
                callbacks = [];
                return this;
            }
        };

    return deferred;
},
/* Class: Deferred.
 *  methods: then, done, fail, resolve, reject, resolveWith, rejectWith, isResolved, 
isRejected, promise
 *
 *  then is a shortcut for both assigning done & fail in one function.
 *
 *  This one has two underlying lists with different semantic meanings. You
 *  can bind to both the done callbacks and the fail callbacks then either
 *  resolve or reject your Deferred object.
 *
 *  You can check whether it has been resolved or rejected. useful to see
 *  Afterwards which one has happened.
 *
 *  Call .promise to return a new object which doesn't have the resolve/reject
 *  methods on it. This means you can only bind to it and not resolve/reject it.
 *  This is effectively read-only.
 *
 */
// Full fledged deferred (two callbacks list)
Deferred: function (func) {
        // the main deferred which deals with the success callbacks
    var deferred = jQuery._Deferred(),
                // the failure deferred which deals with the rejected callbacks
        failDeferred = jQuery._Deferred(),
                // the read only promise is cached.
        promise;
    // Add errorDeferred methods, then and promise
    jQuery.extend(deferred, {
                // def.then([f1, f2, ...], [g1, g2, ...] is a short hand for
                // def.done([f1, f2, ...])
        // def.fail([g1, g2, ...])
        then: function (doneCallbacks, failCallbacks) {
                        // fail exists here because this code will only run after
                        // deferred has been extended.
            deferred.done(doneCallbacks).fail(failCallbacks);
            return this;
        },
                // map def.fail to the second underlying deferred callback list
                // map all the other methods for rejection/failure to the underlying
                // failDeffered object so that Deferred has two callback lists stored
                // internally.
        fail: failDeferred.done,
        rejectWith: failDeferred.resolveWith,
        reject: failDeferred.resolve,
        isRejected: failDeferred.isResolved,
        // Get a promise for this deferred
        // If obj is provided, the promise aspect is added to the object
                // no clue what to do with "i"
        promise: function (obj, i /* internal */ ) {
                        // if no argument is passed then just extend promise
            if (obj == null) {
                                // if cached return the cache.
                if (promise) {
                    return promise;
                }
                                // set promise & arg to be {}
                promise = obj = {};
            }
                        // for each promiseMethods in the read only promise list
            i = promiseMethods.length;
            while (i--) {
                                // set the deferred method on the object
                obj[promiseMethods[i]] = deferred[promiseMethods[i]];
            }
                        // returns the "read-only" deferred without
                        // resolve, resolveWith, reject & rejectWith.
                        // So you cant "resolve" it but only add "done" functions
            return obj;
        }
    });
    // Make sure only one callback list will be used
        // if either resolve or reject is called cancel both.
        // this means that the one that has been called cant be called again
        // and the other one will never be called. So only the done or the fail
        // methods will ever be called
    deferred.then(failDeferred.cancel, deferred.cancel);
        // Don't mess with cancel!
    // Unexpose cancel
    delete deferred.cancel;
    // Call given func if any
        // function argument to be called. This was passed in. Allows you to
        // handle the deferred object after creating a new one, both as this scope
        // and as a new argument.
    if (func) {
        func.call(deferred, deferred);
    }
    return deferred;
},

/* Method: when
 * Arguments: none OR 1 of type(any & !deferred) OR n of type(deferred).
 *
 * If no arguments are passed then it gets resolved immediatly. A good way to
 * call multiple callback functions? Don't really know a good use of $.when()
 *
 * If one argument is passed and its not a deferred object then it resolves
 * immediatly and passes that argument to all the done callbacks attached.
 *
 * if n arguments are passed of type deferred object then the the done callbacks
 * will only fire if all of them succeed. If a single one fails then the
 * fail callbacks fire.
 *
 * Returns a promise read-only deferred object
 */
// Deferred helper
when: function (object) {
    var args = arguments,
        length = args.length,
                // If you pass in a deferred object then set deferred to be the promise
        // if you pass in anything else then set deferred to be a new deferred
        deferred = length <= 1 && object && jQuery.isFunction(object.promise) ?
                object :
                        jQuery.Deferred(),
        // cache the promise
        promise = deferred.promise(),
                // store an array
        resolveArray;

        // if multiple objects are passed in
    if (length > 1) {
                // create an arrey to store of values.
        resolveArray = new Array(length);
                // for each object that we wait on
        jQuery.each(args, function (index, element) {
                        // when that object resolves then
            jQuery.when(element).then(function (value) {
                                // store value in the array or store an array of values in it
                resolveArray[index] = arguments.length > 1 ? slice.call(arguments, 0) : 
value;
                                // if length === 1 then we finished calling them all
                if (!--length) {
                                        // resolve the deferred object with the read only promise
                                        // as context and the resolved values array as the argument
                    deferred.resolveWith(promise, resolveArray);
                }
                        // if any fail then we reject or deferred
            }, deferred.reject);
        });
        // if deferred was newly created but there was only one argument then
    // resolve it immediatly with the argument.
    } else if (deferred !== object) {
        deferred.resolve(object);
    }
        // return the read-only deferred.
    return promise;
},
Raynos
quelle
6
Dies würde viel
besser
@gnarf Problem solved. Übrigens ist das die 1.5beta Quelle. Ich denke, es gibt einige Änderungen in 1.6
Raynos
9

Korrigieren Sie mich, wenn ich falsch liege, aber es hat kürzlich für mich geklickt, dass es sich im Wesentlichen um einen asynchronen Task Runner handelt. Das Versprechen ist ein Ergebnisvertrag, der sicherstellt, dass Sie ... etwas erhalten, aber ohne Garantie, wann Sie es erhalten.

George R.
quelle
Also nur alter Wein in neuer Flasche!
ankush981
3

Während der Arbeit in Javascript stoßen wir auf Situationen, in denen Funktionsaufrufe asynchron sind. Das heißt, der Fluss der Calee-Funktion (sagen wir X) wartet nicht auf die aufgerufene asynchrone Funktion (sagen wir Y). Ein typisches Beispiel ist, wenn wir einen Server aufrufen, um Daten aus einer Datenbank oder einer HTML-Seite abzurufen. Wenn diese Anrufe nicht asynchron waren, bleibt die Benutzeroberfläche hängen und wartet darauf, dass der Server antwortet. Diese asynchrone Natur führt zu einem Problem, wenn Sie Dinge in einer Reihenfolge ausführen möchten, z. B. wenn Sie etwas drucken möchten, nachdem Y (asynchron) ausgeführt oder Daten abgerufen hat. Hier stellt uns jQuery Deffered Object zur Verfügung. Grundsätzlich hat sich jQuery um den gesamten Boilerplate-Code gekümmert, den wir normalerweise schreiben, um diese Situation zu lösen. Hier ist ein einfaches Beispiel:

  $.ajax({
      ...
  }).done(function(){
      //write here what you wish to do when this ajax call is success
  }).fail(function(){
      //write here what you wish to do on failure of this ajax call
  }); //see more on jQuery Deferred page

Sie können Ihre eigene verzögerte (asynchrone) Funktion schreiben

function DoSomethingTimeConsumingAsynch(){
    var deferred = $.Deferred();

    _.defer(function(){ //I am using underscore, you can also use setTimeout
        ...  
        deferred.resolve();//When the process is done successfully 
        ...
        deferred.reject(); //When the process has failed
    });
    return deferred;
}

//HEre how to use your own asynch function
DoSomethingTimeConsumingAsynch()
.done(function(){
   //this will be invoked on success
})
.fail(function(){
   //this will be invoked on failure
})

Ich hoffe das hat geholfen.

Hadaytullah
quelle