14

我有一个标准的 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 的延迟对象,我认为这段代码还有改进的空间。不幸的是,我对它还不是很舒服,我正在努力学习它。

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

这是我的实现的一个jsfiddle

4

3 回答 3

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

不过,不太确定您是否会认为这是一种改进。

于 2011-10-12T18:11:46.043 回答
3

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

http://en.wikipedia.org/wiki/GRASP_(面向对象设计)

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

因此,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/

于 2013-07-13T11:14:32.417 回答
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/

于 2011-10-12T18:33:33.760 回答