Статьи

Изучение отложено и обещать в JQuery


JQuery Deferred объект был представлен как часть версии 1.5 платформы.
Отложенный объект в jQuery основан на концепции
Обещаний . Чтобы понять все об отложенных объектах, было бы разумно попытаться понять, что такое Обещание.

Много раз в нашей повседневной жизни мы не можем полагаться на результаты определенных действий.
Тем не менее, нам все еще может потребоваться принять решение о будущем в зависимости от ожидаемого результата. Все, что мы знаем наверняка, это то, что мы получим результат. Давайте просто скажем, что мы оборачиваем термин «результат» в необычно выглядящую подарочную упаковку и называем его «Обещание».

Итак, что такое «Обещание»?
Что ж, это не что иное, как объект, возвращаемый методом, на основе которого вы можете определить дальнейшие действия.

Давайте возьмем аналогию с реальным миром.

Вы решаете пройти собеседование на работу в компании X — (это функция, которую вы собираетесь выполнять)

Интервью имеет результат.
Вы либо получаете работу, либо нет.

Но вам нужен план, независимо от того, каков будет результат собеседования.
И вам нужно спланировать это сейчас. Вы не можете планировать это в будущем после собеседования. Например, вам может понадобиться забронировать проездной билет или забронировать место для вечеринки, если интервью пройдёт. Если этого не произойдет, вам, возможно, придется купить некоторые книги и обратиться в компанию Y.

Если бы мы написали этот план программно, мы бы написали что-то вроде этого.

candidate.attendInterview().then(
successFunction(){
 //Book tickets.
 //Order a crate of beer for a party!
 },
failureFunction(){
 //Buy some more books to brush up
 //Apply to company Y
}
);

Что интересно в этом, так это то, что даже если вы не знаете точного результата функции serveInterview, вы можете связать метод с «предполагаемым» результатом и указать функции, которые должны быть вызваны после завершения функции serveInterview. , Это возможно, потому что функция serveInterview будет возвращать объект Promise. Интересным аспектом этого паттерна является концепция разрешения обещаний. Говорят, что выполнение обещания происходит, когда задача, которая возвращает обещание, завершается в реальности. Поэтому, когда вы возвращаете объект Promise из функции serveInterview, обещание считается неразрешенным. Только когда результат операции доступен, т.е. когда фактический результат собеседования получен через некоторое время, объект обещания считается разрешенным.После разрешения на основе результата разрешения будет вызвана функция successFunction или failFunction.

Концепция обещаний важна из-за трех основных причин 

  1. Это помогает вам отделить логику будущих обработчиков от фактического вызова задачи. 
  2. Вы можете добавить любое количество будущих обработчиков. 
  3. Если вы добавите обработчик в уже выполненное Обещание, соответствующая (успешное завершение) функция сработает немедленно.

Преимущества использования обещания становятся намного более очевидными при использовании в контексте запросов ajax.
Это связано с тем, что результат запроса ajax будет доступен в будущем, а количество времени, необходимого для завершения запроса ajax, не может быть определено заранее.

jQuery представил объект Deferred в версии 1.5 библиотеки для обработки этого сценария.
Объект Deferred на самом деле является реализацией интерфейса Promise. Он предоставляет все функции Promise, а также позволяет создавать новые отложенные объекты, присоединять к ним будущие обработчики и программно их обрабатывать.

Давайте разберемся с отложенным объектом jQuery в терминах ajax-запроса.

В версиях jQuery до 1.5, чтобы прикрепить обработчик успеха или сбоя к запросу ajax, нужно было бы объявить функцию обратного вызова успеха при определении вызова ajax.
Хотя эта схема намного удобнее, чем под покровом xmlhttprequest, она имела один недостаток. Функция, которая будет вызываться в будущем (успех или неудача), должна быть определена как часть функции ajax. Более того, нельзя было присоединить несколько обработчиков успеха или неудачи к вызову ajax.

Вот как можно запросить использование версии jQuery до 1.5

$.ajax({
  url: "test.html",
  success: function(){
    alert("ajax request succesful");
  },
  failure: function(){
    alert("ajax request failed");
  }
});
As shown in the code above, it can be see that you are bound to specify the success function while making the ajax request. Moreover there is only one available callback for success and failure each.
As of jQuery 1.5, the introduction of the Deferred changed the game drastically. And its a progressive change to be honest, because it not only adds more effectiveness to the ajax functionality, but it also adds more functionality to the framework as a whole. We shall see how.
Let us now invoke the same ajax call that we saw earlier using a version of jQuery > 1.5.

var myRequest = $.ajax({  url: "test.html" })
 .done(function(data){
  alert('ajax request was successful');
  })
 .fail(function(data){
  alert('ajax request failed');
 });
  
 //After a few more lines of code
 myRequest.done(function(data){
  $('div.myclass').html('Ajax Response ' + data);
 });
As you see, this is far better than the previous method of invocation of the ajax request. The done and fail functions are used to register callbacks on the object returned by the ajax call. The ajax call returns a Deferred object which implements the Promise interface. Since its a promise, you can attach handlers to it so that when the request completes, the future handlers would be invoked.
Not only that, as you see in the example, we were also able to attach a new success handler (the argument to the done function) at a later point of time. If, until reaching that line, the ajax request has not been completed, in that case the function will be queued and will be invoked at a later point of time. If the request is already complete, then the function will fire immediately in case of success. In a similar way, you can add multiple functions as a to the failure queue as well.
This approach gives you a lot of flexibility in writing code and also makes the code more legible. The Deferred object has a number of other functions. One of them that is pretty interesting is the when() function. This function has a lot of appeal because it allows you to group together deferred objects and then set up future handlers after all the objects have been resolved or rejected.
This opens up the door to create interfaces that depend upon input from a number of sources but need to be rendered together. For example, a certain div contains information based upon a user choice. And the user selection leads to the firing of 2 different ajax requests. If your data is of the nature that the information would make sense only if the data from both the requests are shown together, then using the when function becomes a perfect candidate for such scenarios.
In the below example, you can issue a request to two different url’s and only after the data from both the url’s is retrieved, the future function that is specified as the argument to ‘done’ will be invoked.

$.when($.ajax("mypage1.html"), $.ajax("mypage2.html")).done(function(a1,  a2){
     $('div.page1details').html(a1[0]);
     $('div.page1details').html(a2[0]);
  });
Observe that there are 2 function calls inside the when function. You can have as many functions as you may like. The only criteria is that the object returned from the function call should be either a Promise or a Deferred. If it is a promise, then well and fine, If it is a Deferred, the promise() function is invoked and the Promise object is retrieved from the deferred object. A parent Deferred object is created and this parent object keeps track of all the deferred objects of the functions defined inside the when function. Once all the functions declared inside the when function resolved, the done function will be invoked. However if any of the functions declared in the when function fails, the failure callbacks are invoked without waiting for the resolution or rejection of the remaining functions.
You can easily use the done and the fail functions to register future callbacks for a deferred object. Another way you can do the same would be to make use of the then function. If you make use of the when — then function combination, it becomes much more easier to read the code, grammatically, because it appears to form some kind of a sentence. Lets see an example of using the when-then pair, and this time we shall also see how one can register multiple callbacks

$(function(){
     
    function fun1(data1, data2){
        console.log("fun1 : " + data1[0].query + " " +
                    data1[0].results.length);
        console.log("fun1 : " + data2[0].query + " " +
                    data2[0].results.length);
    }
     
    function fun2(data1, data2){
        console.log("fun1 : " + data1[0].query + " " +
                    data1[0].results.length);
        console.log("fun1 : " + data2[0].query + " " +
                    data2[0].results.length);    }
     
    function fun3(data){
        console.log("fun3 called upon faliure");
    }
     
    function fun4(data){
        console.log("fun4 called upon faliure");
    }   
     
    var successFunctions = [fun1, fun2];
    var failureFunctions = [fun3, fun4];
     
    $.when(
        $.ajax("http://search.twitter.com/search.json", {
            data: {
                q: 'jquery'
            },
            dataType: 'jsonp'
        })
    ,
        $.ajax("http://search.twitter.com/search.json", {
            data: {
                q: 'blogger'
            },
            dataType: 'jsonp'
        })
    ).then(successFunctions,failureFunctions);
     
});
In the above example, I created 4 functions. 2 of them will be invoked upon success and 2 will be invoked upon failure. As you can see, instead of passing a single function as a parameter to the then(), I passed in an array of functions for the success as well as the failure callbacks. Another point to be noted here is that since we have 2 ajax requests in the when() function, the success and failure methods can accept 2 arguments. Each argument will contain the jqXhr object that was returned by the corresponding ajax call. So, the above example demonstrates how to use multiple callbacks, and and also how to use the data that is obtained from a json ajax request. 
You may also note that since the ajax functions are making an JSONP request, I have referenced the JSON object in the success callbacks using data1[0] and data2[0] respectively. The data1 and data2 objects are actually arrays of the form [JSONObject, «success»,jqXHR]. In case the request was an ordinary ajax request instead of a jsonp request, you would instead have to make use of the jqXHR object and retrieve the responseText as usual. 
Until now we have seen examples where the deferred object was the object that was returned by the ajax call. Although used extensively with ajax, the deferred object can be used in other places as well. An example of that would be running future callbacks after a set of animations have finished executing. You can group together the animations in a when() function and then using a deferred object, you can easily invoke future callbacks using the then() function. 
The catch here is that animations do not return deferred objects. They always return a simple jQuery object. This is where the Deferred constructor comes to the rescue. The deferred constructor can be used to create a new deferred object and then you can wrap your custom code inside the deferred object and return the wrapper instead of the jQuery object. 
Lets see an example for the same. In the following code, we are going to issue an ajax request, and resize a div. After both these tasks are complete, we are going to display the content retrieved via ajax in the div.

$(function(){
 
    function successFunction(data){
        console.log("successfunction");
        $('div.animateMe').html('Results : ' + data[0].results.length);
    }   
     
    function animateDiv(){
        //As per the documentation, the argument that is passed
        //to the constructor is the newly created deferred object
        var dfd = $.Deferred(function(dfd){
            $('div.animateMe').animate({height:'200px'},2000,dfd.resolve);
        });
         
        return dfd;
    }
     
    function failureFunction(){
        console.log("failureFunction");
    }
     
    $.when(
        $.ajax("http://search.twitter.com/search.json", {
            data: {
                q: 'jquery'
            },
            dataType: 'jsonp'
        }),animateDiv()
    ).then(successFunction,failureFunction);
});
You can also see a jsfiddle here
This example is pretty simple because it does nothing but wait for the ajax request as well as the animation to complete before resolving the Deferred object. The main point of interest of this example is the creation of a Deferred object within the animateDiv function. Within this function, we first create a new Deferred object. The constructor of the Deferred object takes a function as a parameter which is invoked just before the constructor is about to return. This function is passed the newly created Deferred object as an argument. Within, this function, we did an animate and upon animation completion, we indicated that the framework resolve the diferred object by passing the resolve function of the diferred object as an argument. In the next line, we simply return the newly created Deferred object.
In the above example, you might have noticed that we made use of a ‘resolve’ function. This function allows you to explicitly resolve a deferred object. While the ability to resolve a deferred object programmatically is desirable for non ajax requests, the same cannot be said for ajax requests. That’s because an ajax request is said to be resolved only when a response is received from the server. So the resolution of an ajax request takes place internally in the ajax function and should not be available to the programmer directly. 
For non ajax requests, as in the example above, the programmer can resolve the deferred object based upon the specific requirement. Since it is the deferred object that has the resolve method and a number of other additional methods, the ajax request actually returns a Promise. This lets you invoke only the methods of the promise interface on the resultant object thereby preventing an explicit invocation of a programmatic resolve before the ajax request actually completes. 
When you are creating an non ajax deferred object, there is another method — reject, which indicates a failure and invokes the functions of the failure queue. Without any doubt, the deferred object has small but useful api. 
Summing It Up

  1. The deferred object in jQuery is an implementation of the Promise specification. This means that you can a promise where-ever a Deferred can be used. 
  2. Future handlers can be added to promise objects. 
  3. The future handlers are invoked upon the resolution(success) or the rejection(failure) of the invoked function. You can attach multiple future handlers to any deferred object. 
  4. The future handlers can receive parameters, which represent the information that was used to resolve the deferred object. 
  5. Non ajax objects can be wrapped in a deferred object by making use of the Deferred constructor. 
  6. The jQuery.when method can be used to group together a number of deferred objects and to attach future handlers to them. 
  7. Success handlers using the when function are invoked once all the deferred objects are resolved. 
  8. Failure handlers using the when function are invoked if any of the deferred object fails irrespective of the status of the remaining deferred objects.
I hope this article has been helpful in helping you gain an understanding of the concept of Promise and Deferred objects. There are a couple of links below that have good explanations of the same. I suggest that you take a look at them too if you still only have a vague idea of things. 
Also, there might be a thing or two, that I might have misunderstood! Make sure you point them out! 
Many thanks ?

Links and References

И тогда самые важные из них всех

 

From http://mycodefixes.blogspot.com/2012/02/jquery-exploring-deferred-and-promise.html