Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use jQuery's deferred object with custom javascript objects?

I have a standard javascript object whose prototype is extended with a .start() method taking 2 callbacks as arguments: success and failure respectively. This method performs some asynchronous processing (it's not AJAX) and depending on the result of this processing it invokes either the success or the failure callbacks.

Here's how this could be schematized:

function MyObject() {
}

MyObject.prototype.start = function(successCallback, errorCallback) {
    (function(s, e) {
        window.setTimeout(function() {
            if (Math.random() < 0.8) {
                s();    
            } else {
                e();    
            }
        }, 2000);    
    })(successCallback, errorCallback);
}

It's not really important the exact processing performed inside the method, only that it is asynchronous and non-blocking. I have no control over the point of time when the start method will finish the processing. I also have no control over the prototype and implementation of this method.

What I have control over is the success and failure callbacks. It is up to me to provide them.

Now I have an array of those objects:

var arr = [ new MyObject(), new MyObject(), new MyObject() ];

The order of elements in this array is important. I need to trigger the .start() method on each element of the array consecutively but only once the previous has completed (i.e. the success callback was called). And if an error occurs (the failure callback is called) I want to stop the execution and no longer invoke the .start method on the remaining elements of the array.

I could implement this naively by using a recursive function:

function doProcessing(array, index) {
    array[index++].start(function() {
        console.log('finished processing the ' + index + ' element');
        if (index < array.length) {
            doProcessing(array, index);
        }
    }, function() {
        console.log('some error ocurred');
    });
}

doProcessing(arr, 0);

This works fine but looking at the jQuery's deferred Object that was introduced in jQuery 1.5 I think that there is a room for improvement of this code. Unfortunately I don't feel very comfortable yet with it and I am trying to learn it.

So my question is is it possible to adapt my naive code and take advantage of this new API and if yes, could you provide me with some pointers?

Here's a jsfiddle with my implementation.

like image 987
Darin Dimitrov Avatar asked Oct 12 '11 17:10

Darin Dimitrov


People also ask

How do you use Deferred in Javascript?

Definition and UsageIf the defer attribute is set, it specifies that the script is downloaded in parallel to parsing the page, and executed after the page has finished parsing. Note: The defer attribute is only for external scripts (should only be used if the src attribute is present).

What is Deferred object in Javascript?

The Deferred object, introduced in jQuery 1.5, is a chainable utility object created by calling the jQuery. Deferred() method. It can register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function.

How use jQuery Deferred and Promise?

If you are creating a Deferred, keep a reference to the Deferred so that it can be resolved or rejected at some point. Return only the Promise object via deferred. promise() so other code can register callbacks or inspect the current state. For more information, see the documentation for Deferred object.


1 Answers

You could do something like this: (jsFiddle)

function MyObject() {
}

MyObject.prototype.start = function(queue) {
    var deferred = $.Deferred();
    //only execute this when everything else in the queue has finished and succeeded
    $.when.apply(jQuery,queue).done(function() { 
        window.setTimeout(function() {
            if (Math.random() < 0.8) {
                deferred.resolve();    
            } else {
                deferred.reject();    
            }
        }, 2000); 
    });
    return deferred;
}

var arr = [ new MyObject(), new MyObject(), new MyObject() ];

var queue = new Array();
$.each(arr, function(index, value) {
    queue.push(value.start(queue)
        .done(function() {
           console.log('succeeded ' + index);
        })
        .fail(function() {
           console.log('failed ' + index);
        }));
});

Not quite sure wether you would consider this an improvement, though.

like image 200
Ewout Kleinsmann Avatar answered Oct 16 '22 11:10

Ewout Kleinsmann