1

我正在尝试使用 jQuery.Deferrd 对象同步多个 ajax 回调。显然 jQuery.when 会为您处理这个问题,但是我的代码的架构方式是不会以相同的方法调用 ajax 请求。例如,这是流程:

// 一个按钮被点击

// 模块 1 请求一段 html 并更新 DOM

// 模块 2 请求不同的 html 片段并更新 DOM

我需要两个模块同时更新 DOM,这意味着我需要确保在两个请求都返回后运行回调。

模块 1 和模块 2 需要能够彼此独立存在,并且不应该相互了解,因此无法使用 $.when(doMod1Request(), doMod2Request()).then(function () 一起发出请求{ ... }) 并且回调也应该是独立的。

因此,我编写了一个围绕 ajax 的包装器,它将回调添加到延迟对象,并以与 $.when 类似的方式在 ajax 请求返回与延迟对象上的回调次数相同的次数后解析延迟对象.

然而,我的困境是 deferred.resolve() 只能用一组参数调用,所以每个回调都得到相同的值。

例如

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

这是可能的还是我做错了?

4

3 回答 3

1

我会说这是完全有效的。假设你的独立模块,你会做(有两个承诺):

doMod1Request().done(doMod1Update);
doMod2Request().done(doMod2Update);

现在,如果您想一起执行更新,并且只有两个请求都成功,只需编写

$.when(doMod1Request(), doMod2Request()).done(function(mod1result, mod2result) {
    doMod1Update(mod1result);
    doMod2Update(mod2result);
});

如果你用多个参数调用你的解析函数,这只会变得丑陋,因为 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);
});
于 2012-11-22T03:18:22.000 回答
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_2MODULE_3相同的延迟对象传递给window.request它们,一旦两个请求都成功返回,它们就会被解析。

这是我的最终实现: https ://github.com/richardscarrott/ply/blob/master/src/ajax.js

于 2012-12-04T00:54:05.573 回答
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);
            });
        });
    });
});

正如你所看到的,我在这里做了很多假设,但你应该对如何进行有所了解。

于 2012-11-22T03:09:54.150 回答