我有一个标准的javascript对象,其原型使用.start()
方法进行扩展,分别以2个回调作为参数:success
和failure
。此方法执行一些异步处理(它的不 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方法完成处理的时间点。我也无法控制这种方法的原型和实现。
我可以控制的是success
和failure
回调。我应该提供它们。
现在我有一个这些对象的数组:
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。
答案 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/