我正在尝试使用jQuery.Deferrd对象同步多个ajax回调。显然jQuery.when会为你处理这个问题但是我的代码的架构是这样的,即ajax请求不会在同一个方法中调用。例如,这就是流程:
//点击按钮
//模块1请求一段html并更新DOM
//模块2请求不同的html片段并更新DOM
我需要两个模块同时更新DOM,这意味着我需要确保在两个请求都返回后运行回调。
模块1和模块2需要能够彼此不存在,并且彼此之间不应该彼此了解,因此请求不能使用$ .when(doMod1Request(),doMod2Request())一起进行。然后( function(){...})和回调也应该是独立的。
因此,我编写了一个围绕ajax的包装器,它将回调添加到延迟对象,并以类似于$。的方式解析延迟对象,一旦ajax请求返回的次数与回调次数相同。延期对象。
然而,我的困境是deferred.resolve()只能用一组参数调用,所以每个回调得到的值都是相同的。
e.g。
var deferred = new $.Deferred();
deferred.done(function (response) {
console.log(response); // <div class="html-snippet-1"></div>
});
deferred.done(function (response) {
console.log(response); // <div class="html-snippet-1"></div>
});
deferred.resolve('<div class="html-snippet-1"></div>');
虽然我想要这样的东西:
var deferred = new $.Deferred();
deferred.done(function (response) {
console.log(response); // <div class="html-snippet-1"></div>
});
deferred.done(function (response) {
console.log(response); // <div class="html-snippet-2"></div>
});
deferred.resolve(['<div class="html-snippet-1"></div>', '<div class="html-snippet-2"></div>']);
这是可能的还是我错误地解决了这个问题?
答案 0 :(得分:1)
我认为这完全有效。假设你的独立模块,你会做(有两个Promises):
doMod1Request().done(doMod1Update);
doMod2Request().done(doMod2Update);
现在,如果你想一起执行更新,并且只有当两个请求都成功时,才写下
$.when(doMod1Request(), doMod2Request()).done(function(mod1result, mod2result) {
doMod1Update(mod1result);
doMod2Update(mod2result);
});
如果你用多个参数调用你的resolve函数,这只会变得很难看,因为jQuery在那里有点不一致,并且没有真正区分多个参数和一个数组参数。
要使用您正在使用的发布 - 订阅模式将它们解开,我建议如下:
function Combination() {
this.deferreds = [];
this.success = [];
this.error = [];
}
Combination.prototype.add = function(def, suc, err) {
this.deffereds.push(def);
this.success.push(suc);
this.error.push(err);
};
Combination.prototype.start = function() {
var that = this;
return $.when.apply($, this.deferreds).always(function() {
for (var i=0; i<that.deferreds.length; i++)
that.deferreds[i].done(that.success[i]).fail(that.error[i]);
// of course we could also call them directly with the arguments[i]
});
};
// Then do
var comb = new Combination();
window.notifyModules("something happened", comb); // get deferreds and handlers
comb.start();
// and in each module
window.listen("something happended", function(c) {
c.add(doRequest(), doUpdate, doErrorHandling);
});
答案 1 :(得分:0)
让我们假设您的模块看起来像这样:
var MODULE_1 = function() {
function getSnippet() {
return $.ajax({
//ajax options here
});
}
return {
getSnippet: getSnippet
}
}();
var MODULE_2 = function() {
function getSnippet() {
return $.ajax({
//ajax options here
});
}
return {
getSnippet: getSnippet
}
}();
不要担心,如果你的模块不同,重要的是getSnippet函数每个返回一个jqXHR对象,从jQuery 1.5开始实现Promise接口。
现在,让我们假设您想要获取两个片段以响应某些事件(例如按钮单击)并在收到两个ajax响应时执行某些操作,然后点击处理程序将是这样的:
$("myButton").on('click', function(){
var snippets = [];
var promises_1 = MODULE_1.getSnippet().done(function(response){
snippets.push({
target: $("#div_1"),
response: response
});
});
var promise_2 = MODULE_2.getSnippet().done(function(response){
snippets.push({
target: $("#div_2"),
response: response
});
});
$.when(promise_1, promise_2).done(function() {
$.each(snippets, function(i, snippetObj) {
snippetObj.target.html(snippetObj.response);
});
});
});
稍微复杂一些,如果你有许多类似构造的模块来获取许多片段,那就更好了,就像这样:
$(function(){
$("myButton").on('click', function(){
var promises = [];
var snippets = [];
var modules = [MODULE_1, MODULE_2, MODULE_3 .....];
for (var i=1; i<=10; i++) {
promises.push(modules[i].getSnippet().done(function(response){
snippets.push({
target: $("#div_" + i),
response: response
};
}));
}
$.when.apply(this, promises).done(function() {
$.each(snippets, function(i, snippetObj) {
snippetObj.target.html(snippetObj.response);
});
});
});
});
正如你所看到的,我在这里做了很多假设,但你应该知道如何继续。
答案 2 :(得分:0)
为了确保每个回调都传递了我做过以下的适当参数:
var guid = 0,
deferreds = [];
window.request = function (url, deferred, success) {
var requestId = guid++;
if ($.inArray(deferred) === -1) {
deferreds.push(deferred);
$.extend(deferred, {
requestCount: 0,
responseCount: 0,
args: {}
});
}
deferred.requestCount++;
deferred
.done(function () {
// Corresponding arguments are passed into success callback using requestId
// which is unique to each request.
success.apply(this, deferred.args[requestId]);
});
$.ajax(url, {
success: function () {
// Store arguments on deferrds args obj.
deferred.args[requestId] = arguments;
deferred.responseCount++;
if (deferred.requestCount === deferred.responseCount) {
deferred.resolveWith(this);
}
}
});
};
所以参数是通过闭包来管理的。这允许我确保两个模块彼此不了解,并且如果另一个模块不存在则不会中断,例如:
var MODULE_1 = function () {
$(".myButton").on('click', function() {
// Cross module communication is achieved through notifications.
// Pass along a new deferred object with notification for use in window.request
window.notify('my-button-clicked', new $.Deferred);
});
}();
var MODULE_2 = function () {
// run get snippet when 'my-button-clicked' notification is fired
window.listen('my-button-clicked', getSnippet);
function getSnippet (deferred) {
window.request('/module2', deferred, function () {
console.log('module2 success');
});
}
}();
var MODULE_3 = function () {
// run get snippet when 'my-button-clicked' notification is fired
window.listen('my-button-clicked', getSnippet);
function getSnippet (deferred) {
window.request('/module3', deferred, function () {
console.log('module3 success');
});
}
}();
以上允许每个模块独立运行,这意味着一个模块可以在没有另一个模块松散地耦合代码的情况下工作,因为MODULE_2
和MODULE_3
将相同的延迟对象传递给window.request
它们将两个请求成功返回后即可解决。
这是我的最终实施: https://github.com/richardscarrott/ply/blob/master/src/ajax.js