如何使用jQuery的自定义javascript对象的延迟对象?

时间:2011-10-12 17:19:09

标签: javascript jquery jquery-deferred

我有一个标准的javascript对象,其原型使用.start()方法进行扩展,分别以2个回调作为参数:successfailure。此方法执行一些异步处理(它的 AJAX),并根据此处理的结果调用成功或失败回调。

以下是这可以如何模式化:

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);
}

在方法内部执行的确切处理并不重要,只是它是异步和非阻塞的。我无法控制start方法完成处理的时间点。我也无法控制这种方法的原型和实现。

我可以控制的是successfailure回调。我应该提供它们。

现在我有一个这些对象的数组:

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

此数组中元素的顺序很重要。我需要连续地在数组的每个元素上触发.start()方法,但只在前一个元素完成后触发(即调用成功回调)。如果发生错误(调用失败回调),我想停止执行,不再对数组的其余元素调用.start方法。

我可以通过使用递归函数来实现这个:

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);

这很好但是看看jQuery 1.5中引入的jQuery's deferred Object我觉得这个代码还有改进的余地。不幸的是,我感觉不舒服,我正在努力学习它。

所以我的问题是是否有可能调整我的天真代码并利用这个新的API,如果是的话,你可以给我一些指示吗?

这是我实施的jsfiddle

3 个答案:

答案 0 :(得分:4)

您可以这样做:(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);
        }));
});

不确定你是否会认为这是一种改进。

答案 1 :(得分:3)

当我们编程时,要记住GRASP原则或指南非常重要。

http://en.wikipedia.org/wiki/GRASP_(object-oriented_design)

获得高内聚和低耦合意味着我们的代码将更好,更可重用且更易于维护。

因此,类MyObject必须不知道队列存在。 MyObject将知道自己的功能和方法等等。

// Class MyObject

function MyObject(name) {
    this.name = name;
}

MyObject.prototype.start = function() {

    var deferred = $.Deferred();
    var self = this;
    setTimeout(function() {
        if (Math.random() <= 0.8) {
            console.log(self.name + "... ok");
            deferred.resolve();
        } else {
            console.log(self.name + "... fail");
            deferred.reject();
        }
    }, 1000);

    return deferred.promise();
} 

main / caller函数将知道MyObject存在,它将创建三个将按顺序执行的实例。

// Create array of instances
var objectArray = [ new MyObject("A"), new MyObject("B"), new MyObject("C") ];

// Create array of functions to call start function
var functionArray = [];
$.each(objectArray, function(i, obj) {
    functionArray.push(
        function() {
           return obj.start();
        }
    );
});

// Chain three start calls 
$.iterativeWhen(functionArray[0], functionArray[1], functionArray[2])
.done(function() {

    console.log("First: Global success");

    // Chain three start calls using array
    $.iterativeWhen.apply($, functionArray)
    .done(function() {
        console.log("Second: Global success");
    })
    .fail(function() {
        console.log("Second: Global fail");
    });

})
.fail(function() {
    console.log("First: Global fail");
});

我为jQuery构建了一个插件:iterativeWhen。它适用于jQuery 1.8及更高版本。

$.iterativeWhen = function () {

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

    $.each(arguments, function(i, obj) {

        promise = promise.then(function() {
            return obj();
        });
    });

    deferred.resolve();

    return promise;
}; 

Jsfiddle:http://jsfiddle.net/WMBfv/

答案 2 :(得分:2)

您的实施没有任何问题。众所周知,使用jQuery并不总是最好的方法。

我这样做:(无需修改MyObject类..)

function doProcessing(array, index) {
    var defer = new $.Deferred();

    $.when(defer).then(doProcessing);

    array[index++].start(function() {
        log('finished processing the ' + index + ' element');
        if (index < array.length) {
            defer.resolve(array, index);
        }
    }, function() {
        log('some error ocurred => interrupting the process');
    });
};

正如您所看到的,与纯JavaScript方法相比,没有任何优势。 :)

这是我的小提琴:http://jsfiddle.net/jwa91/EbWDQ/