0

オートコンプリートの結果を返すために呼び出される関数のキューを作成しようとしています。それらのいくつかは $.getJSON 呼び出しから自分で構築した関数であり、いくつかはjQuery UI autocomplete に指定されたものを使用して外部開発者から提供されました。

たとえば、ここに提供されている偽物があります。それが本当に非同期なのか、それともいつコールバックを呼び出す可能性があるのか​​ 、前もってわかりません。

var providedFunction = function(search, response) {
    setTimeout(function() {
        var arr = ['One', 'Two', 'Demo Custom'];
        response($.grep(arr, function (s) { return s.indexOf(search) === 0; }));
    },5000);
};

次に、それをいくつかの他の $.getJSON 呼び出しと組み合わせて、リスト全体が終了するまで続行しないようにします。

var searchTerm = "Demo";
var allResults = [];
var functionQueue = [];

functionQueue.push(
    $.getJSON( 'http://ws.geonames.org/searchJSON?featureClass=P&style=short&maxRows=50&name_startsWith=' + searchTerm)
    .success( function(data) {
        $.each(data.geonames, function(i,d) { 
            allResults.push(d.name); });
    })
);

functionQueue.push(
    providedFunction(searchTerm, function(data) {
        allResults.push.apply(allResults, data);
    })
);

// wait for all asyc functions to have added their results,
$.when.apply($, functionQueue).done(function() {
    console.log(allResults.length, allResults);
});                

問題は、提供された関数が完了するまで $.when が待機しないことです。すべての $.getJSON 呼び出しが完了するとすぐに戻ります。明らかに、提供された機能を正しく配線していませんが、その方法がわかりません。

4

1 に答える 1

2

$.when を使用したい場合は、deferred の配列を作成する必要があるため、提供された関数を次のように呼び出すことができます。

functionQueue.push( (function(){
        var df = new $.Deferred();
        providedFunction(searchTerm, function(data) {
            allResults.push.apply(allResults, data);
            df.resolve();
        })
        return df;
    })()
);

もちろん、あなたが本当に凝っていると感じるなら、この便利なユーティリティを使用して、コールバック ベースの API を promise/deferred ベースの API にキャストすることができます。

function castAPItoDeferred(origFunction){
    return function(){
        var df = new $.Deferred(),
            args = Array.prototype.slice.call(arguments, 0);
        // assume that the API assumes the last arg is the callback
        args.push(function(result){
            df.resolve(result);
        });
        try {
            origFunction.apply(null, args);
        } catch(e) {
           df.reject(e);
        }
        return df;
    }
}

これにより、次のような素晴らしいことができます:

providedFunctionAsDeferred = castAPItoDeferred(providedFunction);

functionQueue.push(
    providedFunctionAsDeferred(searchTerm)
    .success( function(data) {
        allResults.push.apply(allResults, data);
    })
);

最後の警告 - 2 番目のルートを下る場合は、API 関数がオブジェクトで呼び出される場合は、それらをヒッチ/バインドすることを忘れないでください ( などmyApi.doSomeAsyncThing) 。

最後に、 $.when を使用する代わりに、おそらくカウンターを使用して手動で追跡することもできます。例:

var counter = 2; // set your number of ops here

var checkIfFinished = function(){
    if(--counter === 0) {
        triggerAllEventsCompleteFunc(); // replace this with your desired action
    }
}

// call checkIfFinished in all your callbacks
于 2013-04-01T20:29:33.550 に答える