1

jQuery.Deferrdオブジェクトを使用して複数のajaxコールバックを同期しようとしています。明らかに、jQuery.whenはこれを処理しますが、私のコードは、ajaxリクエストが同じメソッドで呼び出されないように設計されています。たとえば、これはフローです。

//ボタンがクリックされます

//モジュール1はhtmlのスニペットを要求し、DOMを更新します

//モジュール2はHTMLの別のスニペットを要求し、DOMを更新します

DOMを同時に更新するには、両方のモジュールが必要です。つまり、両方のリクエストが返された後にコールバックが実行されるようにする必要があります。

モジュール1とモジュール2は、お互いがなくても存在できる必要があり、お互いの知識がないため、$。when(doMod1Request()、doMod2Request())。then(function()を使用してリクエストを一緒に行うことはできません。 {...})そしてコールバックも独立している必要があります。

したがって、私はajaxのラッパーを作成しました。これは、遅延オブジェクトにコールバックを追加し、$と同様の方法で、ajaxリクエストが遅延オブジェクトのコールバックの数と同じ回数を返したときに遅延オブジェクトを解決します。 。

ただし、私のジレンマはdeferred.resolve()は、1セットの引数でのみ呼び出すことができるため、各コールバックは同じ値を取得します。

例えば

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

これは完全に有効だと思います。独立したモジュールを想定すると、次のようになります(2つのPromiseを使用)。

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

ここで、更新を一緒に実行したい場合で、2つの要求が両方とも成功した場合にのみ、次のように記述します。

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

これは、複数の引数を使用してresolve関数を呼び出す場合にのみ見苦しくなります。これは、jQueryが少し一貫性がなく、複数の引数を1つの配列引数から実際に区別しないためです。

使用しているパブリッシュ/サブスクライブパターンとそれらを切り離すには、次のことをお勧めします。

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 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インターフェイスを実装します。

ここで、何らかのイベント(ボタンのクリックなど)に応答して2つのスニペットをフェッチし、両方の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 に答える
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_3window.request

これが私の最終的な実装でした: https ://github.com/richardscarrott/ply/blob/master/src/ajax.js

于 2012-12-04T00:54:05.573 に答える