11

私のプログラムのほとんどすべての関数には何らかの非同期呼び出しがありますが、それらはすべて前の関数の結果に依存しています。そのため、次の関数呼び出しを次のように個々の関数にハードコーディングしました。

function getStuff() {
    $.ajax({
        ...
        success: function(results) {
            // other functions involving results
            getMoreStuff(results);
        }
    });
}

function getMoreStuff(results) {
    $.ajax({
        ...
        success: function(moreResults) {
            // other functions involving moreResults
            doSomethingWithStuff(moreResults);
        }
    );
}

等々。これは、各関数が次の関数を呼び出す大きなチェーンです。これはプログラム内で機能しますが、各機能を個別に役に立たなくします。

この問題を回避する方法が少しわかりません。関数呼び出しを行うと、(上記の関数を使用して)次のようになるため、一般的なコールバック関数の使用方法がわかりませんでした。

getStuff(function() {
    getMoreStuff(results, doSomethingWithStuff);
};

しかし、「結果」はまだ定義されていません。

解決策は明白に思えますが、私はそれについて少しばかり話しています。ごめん!

4

4 に答える 4

11

概要

いくつかの選択肢があります。コールバックを使用して、これらの関数を使用するコードを次のようにすることができます。

getStuff(function(results) {
    getMoreStuff(results, doSomethingWithStuff);
});

または、jQueryDeferredPromiseオブジェクトを使用して、次のようにします。

getStuff().then(getMoreStuff).then(doSomethingWithStuff):

コールバックの使用

両方getStuffを持ちgetMoreStuff、完了時に呼び出すコールバックである引数を受け入れます。次に例を示します。

function getStuff(callback) {
//                ^------------------------------ callback argument
    $.ajax({
        ...
        success: function(results) {
            // other functions involving results
            callback(results);
//          ^------------------------------------ use the callback arg
        }
    });
}

...同様にgetMoreStuff.

Deferredと_Promise

jQuery のajax機能は、jQuery の機能と統合されていDeferredますPromise。既存の関数に追加returnして、それを機能させることができます。たとえば、次のようになります。

function getStuff(callback) {
    return $.ajax({
        ...
    });
}

(注:successコールバックは必要ありません。)

次に、このコード:

getStuff().then(getMoreStuff).then(doSomethingWithStuff);

これを行います:

  1. getStuffその呼び出しを開始し、その呼び出しが作成するajaxを返しPromiseます。

  2. そのajax呼び出しが完了して promise を解決するとgetMoreStuff、呼び出しの結果がajax最初の引数として呼び出されます。呼び出しを開始 ajaxます。

  3. getMoreStuffajax呼び出しが完了すると、その呼び出しdoSomethingWithStuffの結果( 内のもの) とともに呼び出されます。getMoreStuff

各段階で正しい結果が渡されるようにするにthenは、 ではなくを使用することが重要です。done( を使用すると、 とdoneの両方getMoreStuff の呼び出しdoSomethingWithStuffの結果が表示されます。)getStuffajax

を使用した完全な例を次に示しajaxます。

フィドル| 呼び出しごとに 1 秒かかる代替 Fiddleajax (何が起こっているかを簡単に確認できます)

function getStuff() {
    display("getStuff starting ajax")
    return $.ajax({
        url: "/echo/json/",
        type: "POST",
        data: {json: '{"message": "data from first request"}'},
        dataType: "json"
    });
}

function getMoreStuff(results) {
    display("getMoreStuff got " + results.message + ", starting ajax");
    return $.ajax({
        url: "/echo/json/",
        type: "POST",
        data: {json: '{"message": "data from second request"}'},
        dataType: "json"
    });
}

function doSomethingWithStuff(results) {
    display("doSomethingWithStuff got " + results.message);
}

getStuff().then(getMoreStuff).then(doSomethingWithStuff);

function display(msg) {
    var p = document.createElement('p');
    p.innerHTML = String(msg);
    document.body.appendChild(p);
}

出力:

getStuff 開始 ajax

getMoreStuff は最初のリクエストからデータを取得し、ajax を開始しました

doSomethingWithStuff は 2 番目のリクエストからデータを取得しました

ajaxこの利点を得るため に を使用する必要はありません。独自のDeferredandPromiseオブジェクトを使用できます。これにより、次のようなチェーンを記述できます。

one().then(two).then(three);

...非同期の完了が発生する可能性があるあらゆる状況に対応します。

これは非ajax例です:

フィドル

function one() {
    var d = new $.Deferred();
    display("one running");
    setTimeout(function() {
      display("one resolving");
      d.resolve("one");
    }, 1000);
    return d.promise();
}

function two(arg) {
    var d = new $.Deferred();
    display("Two: Got '" + arg + "'");
    setTimeout(function() {
      display("two resolving");
      d.resolve("two");
    }, 500);
    return d.promise();
}

function three(arg) {
    var d = new $.Deferred();
    display("Three: Got '" + arg + "'");
    setTimeout(function() {
      display("three resolving");
      d.resolve("three");
    }, 500);
    return d.promise();
}

one().then(two).then(three);

function display(msg) {
    var p = document.createElement('p');
    p.innerHTML = String(msg);
    document.body.appendChild(p);
}

出力:

ランニング

解決する

2: 「1」を取得

解決する

3: 「2」を取得

解決する

これら 2 つ (ajax例と非例ajax) は、必要に応じて組み合わせることができます。たとえば、getStuffこのajax例を参考にして、データを に渡す前に何らかの処理を行う必要があると判断した場合、次のgetMoreStuffように変更します

function getStuff() {
    // Create our own Deferred
    var d = new $.Deferred();
    display("getStuff starting ajax")
    $.ajax({
        url: "/echo/json/",
        type: "POST",
        data: {json: '{"message": "data from first request"}', delay: 1},
        dataType: "json",
        success: function(data) {
            // Modify the data
            data.message = "MODIFIED " + data.message;

            // Resolve with the modified data
            d.resolve(data);
        }
    });
    return d;
}

変更されていない使用方法に注意してください。

getStuff().then(getMoreStuff).then(doSomethingWithStuff);

変更されたのは 内だけgetStuffでした。

これは、「プロミス」の概念全体に関する優れた点の 1 つです (これは jQuery に固有のものではありませんが、jQuery は使用するのに便利なバージョンを提供します)。

于 2013-08-09T10:16:02.477 に答える
5

試す

function getStuff() {
    return $.ajax({
        ...
        success: function(results) {
            // other functions involving results
        }
    });
}

function getMoreStuff(results) {
    return $.ajax({
        ...
        success: function(moreResults) {
            // other functions involving moreResults
        }
    );
}

それで

getStufff().done(function(){
    getMoreStuff().done(doSomethingWithStuff)
})

于 2013-08-09T10:12:49.970 に答える
4

パラメータを受け入れるコールバックを渡します。

function getStuff( callback ) {
    $.ajax({
        ...
        success: function(results) {
            // callback with result
            callback(results);
        }
    });
}

function getMoreStuff(results, callback) {
    $.ajax({
        ...
        success: function(moreResults) {
            // callback with result
            callback(moreResults);
        }
    );
}

function doSomethingWithStuff(results, callback) {
    // process results via some means not described herein :)
    if (callback){
        // callback yet again with results, but only if callback provided this time
        callback(stillMoreResults);
    }
}

次に、次のようなものを使用します。

getStuff(function(results) { 
    getMoreStuff(results, function(moreresults){
             doSomethingWithStuff(moreresults);
        });
    };

このパターンは一般に、あらゆる非同期操作に役立ちます。これは Ajax 呼び出しに固有のものではありません (私はこれを使用して、JQuery で完全なアニメーション ボード ゲームを作成しました)。

于 2013-08-09T10:14:06.293 に答える
2

解決策は非常に簡単です。Publish–subscribeパターンを使用する必要があります。jQuery を使用した最も簡単な実装:

$('body').trigger('joined-game', [game_id, response]);

最初の引数は公開するイベント名、2 番目の引数はデータ配列です。

ベスト プラクティスは、最も具体的な DOM 要素でイベントをトリガーすることですが、複数のページで同じイベントをサブスクライブし、DOM 要素がすべてのページに存在するかどうかわからない場合は、それをトリガーするbodyか、いくつかの「ダンプ/合成」でトリガーできます。見えない DOM 要素は常にすべてのページに存在します。

$("body").on('joined-game', function(event, game_id, response){
    //...
});

次に、利用したいイベントをサブスクライブします。データに加えて、最初の引数は常にイベントであることを忘れないでください。

このソリューションのもう 1 つの利点は、コードを複数のファイルに分割できることです。

詳細: http://webability.pl/en/blog/entry/chaining-javascript-functions-without-dependecy-hell

于 2013-08-09T11:36:28.840 に答える