📜 ⬆️ ⬇️

jQuery Deferred Object (detailed description)

On January 31, jQuery 1.5 was released, one of the key innovations of which was the tool Deferred Object. It is about him that I want to tell more in this article.

This new library functionality is aimed at simplifying work with deferred callbacks. Deferred Object, similar to the jQuery object, is “chainable” (chainable), but has its own set of methods. Deferred Object is able to register multiple handlers in a queue, call handlers registered in a queue, and switch the state to “completed” or “error” for synchronous or asynchronous functions.

general description


The idea is quite simple: you need to create a Deferred Object, give this object to some external code that can hook handlers to it (or not hook), and then initiate the execution of these handlers by calling just one object method.

The whole process of using the Deferred Object can be divided into three global stages:

The word “queue” in the description of Deferred Object is not just used - the handlers added to the queue are called in the order in which they were added (those who were added earlier will be called earlier).
')
When adding handlers, it is not necessary to check the state of the deferred object (or if it has already been completed or canceled). If the "run" / "error" handler is added to an already "completed" / "canceled" object, it will be called immediately.

Re-call the resolve / reject of the object deferred does not lead to a change in its state and re-call handlers, but is simply ignored (and it doesn’t matter what the resolve () or reject () was called before).

Deferred Object is “chained”, which means that its methods (not all) return a reference to the source object, which makes it possible to call several methods of the object in a row, separating them with a full stop. For example, like this: deferred.done (callback1) .fail (callback2);

Description of methods


deferred.done (doneCallbacks)
adds a handler that will be called when the deferred object goes into the "completed" state

deferred.fail (failCallbacks)
adds a handler that will be called when the deferred object goes into the "canceled" state

deferred.then (doneCallbacks, failCallbacks)
adds handlers at once of both types described above, equivalent to writing deferred.done (doneCallbacks) .fail (failCallbacks)

In the three methods described above, the doneCallbacks, failCallback arguments can be separate functions or arrays of functions.

deferred.resolve (args)
puts the deferred object in the "completed" state and calls all doneCallbacks handlers with args parameters (the usual listing of parameters is separated by commas)

deferred.reject (args)
sets the deferred object to the "canceled" state and calls all failCallbacks handlers with args parameters

The call context (this inside the function) of the handlers when using the two methods described above will be the projection of the deferred object (or the object itself, if the projection cannot be created) (for the deferred projection, see further in the description deferred.promise ()). If it is required to start handler functions with a different call context, then the following two methods should be used:

deferred.resolveWith (context, args)
deferred.rejectWith (context, args)
methods are completely analogous to .resolve (args) and .reject (args), only the context will be available inside the handler functions via the this keyword

You can check the status of a deferred object using the methods:

deferred.isResolved ()
deferred.isRejected ()
methods return true or false depending on the current state of the object. At the time of execution of the handlers (but the latter has not yet been executed), the corresponding method returns true.

Creating a projection using the deferred.promise () method


This method creates and returns a “projection” of a deferred object — it is a kind of copy of an object that has methods for adding handlers and checking state. Those. we get a deferred object that works with the same queue of handlers as the original, allows adding handlers to it, viewing the state of the original object, but does not allow changing the state of the original object.

Creating a projection is necessary when it is necessary to give an external code the ability to add handlers to the queue and at the same time protect against unauthorized calling of methods for changing the state of an object.

Creating a Deferred Object


You can create a deferred object in two ways: create a new instance of your own using the $ .Deferred () method (the new keyword is optional) or call the $ .when (args) method to use the previously created deferred objects.

$ .Deferred (func)

The method returns a new deferred object. The func argument is optional; you can pass to it a function that initializes the created object before returning it from the constructor. Inside the func function, you can access the object being initialized by deferred through this and / or through the first function argument.

$ .when (deferred1, deferred2, ...)

This method is used to create a deferred object that goes into the "completed" state after all its arguments (deferred objects) go into this state, or go into the "canceled" state as soon as at least one of its arguments is canceled.

If you want to wait for the execution of all tasks and only then complete our tasks, then in this case the place is for this method.
Example 1
$.when($.get( '/echo/html/' ), $.post( '/echo/json/' )).done( function (args1, args2) { alert( " " ); });

This code will display the message “download completed” only after both pages have been successfully loaded into the specified blocks.

If the argument of $ .when () is not a deferred object, then it is considered that this task has already been successfully completed and the program's program is oriented to the remaining arguments.

When a deferred object obtained by the $ .when () method becomes "executed", the call context (this within the function) of the handlers will be the projection of this deferred object, and the arrays in which the corresponding execution arguments passed to $ will be passed to the handlers .when () deferred objects (or one argument itself, if it is one). In the example above, args1 is an array of arguments to the success call for the first ajax request, and args2 for the second.

Use Deferred Object in $ .ajax ()


In jQuery 1.5, the ajax engine was radically rewritten and now the $ .ajax () method and its shortened versions also use deferred objects. This makes it possible to assign quite a few simple handlers to request completion events (success or failure), as well as assign handlers to complete several parallel requests.

From the methods $ .ajax (), $ .get (), $ .post (), $ .getScript (), $ .getJSON () returns an object that has a projection of the deferred object, i.e. it has methods that allow you to add handlers to the queue and use this object as the $ .when () argument.

In addition to this object, synonyms of methods have been added, which by names are more appropriate to the subject of ajax requests:

Interesting Facts


The $ .when () method, when evaluating its arguments that they belong to deferred objects, checks whether they have a .promise () method, and if there is such a method, then even the “left” object is considered deferred. A bug is associated with this, when referring to such “left” objects as if they are deferred leads to execution errors.

In the latest development versions of jQuery (jquery-git.js), the deferred object also has the .invert () method, which, similar to the .promise () method, creates a projection of the object only with the opposite methods: done <=> fail, isResolved < => isRejected, promise <=> invert. The application of this method has not yet been noticed by me

Examples of using


In example 1. (see above) a solution to a typical problem of tracking the completion of several ajax requests is already shown.

Example 2
// 3
function test() {
var d = $.Deferred();
setTimeout( function () { d.resolve(); }, 3000);
return d.promise();
}

var t = test().done( function () { alert( " " ); });

//
setTimeout( function () {
alert( " " );
t.done( function () { alert( "" ); });
}, 5000);


* This source code was highlighted with Source Code Highlighter .

This is a purely demo example, a deferred object is constructed, the state of which is translated to “completed” after 3 seconds, and issued to an external code. In the external code, one handler is added immediately - it is called after 3 seconds, and the second 5 seconds after construction, when the state is already “completed”, so it is called immediately when added.

Example 3
< div id ="d1" style ="width:100px;height:100px;background:red;" > 1 </ div >
< div id ="d2" style ="width:100px;height:100px;background:green;" > 2 </ div >


* This source code was highlighted with Source Code Highlighter .

//
var a1 = $.Deferred(),
a2 = $.Deferred();

$( '#d1' ).slideUp(2000, a1.resolve);
$( '#d2' ).fadeOut(4000, a2.resolve);

a1.done( function () { alert( 'a1' ); });
a2.done( function () { alert( 'a2' ); });
$.when(a1, a2).done( function () { alert( 'both' ); });


* This source code was highlighted with Source Code Highlighter .

This code provides an example of solving a more real problem when you need to execute some code after completing several animations. It is quite simple to add an animation completion handler on one element (this is also used), but it is somewhat more difficult to track the completion of independent animations. Using deferred objects and $ .when (), the code becomes straightforward.

All examples can be run and tested on the jsfiddle.net service .

Materials:
blog.jquery.com/2011/01/31/jquery-15-released
api.jquery.com/category/deferred-object
www.erichynds.com/jquery/using-deferreds-in-jquery (translation: habrahabr.ru/blogs/jquery/112960 )
JQuery 1.5 source code and documentation

Source: https://habr.com/ru/post/113073/


All Articles