web-dev-qa-db-ja.com

jQueryの遅延はキャンセルできますか?

遅延オブジェクトをキャンセルしたい状況があります。遅延はajax呼び出しに関連付けられています。

なぜ遅延オブジェクトを使用しているのか

$ .ajaxによって返される通常のxhrオブジェクトは使用しません。私はjsonpを使用しています。つまり、エラー処理にHTTPステータスコードを使用できず、それらを応答に埋め込む必要があります。次にコードが検査され、関連付けられた据え置きオブジェクトが解決済みまたは拒否済みとしてマークされます。これを行うカスタムAPI関数があります。

_function api(options) {
  var url = settings('api') + options.url;
  var deferred = $.Deferred(function(){
    this.done(options.success);
    this.fail(options.error);
  });
  $.ajax({
    'url': url,
    'dataType':'jsonp',
    'data': (options.noAuth == true) ? options.data : $.extend(true, getAPICredentials(), options.data)
  }).success(function(jsonReturn){
    // Success
    if(hasStatus(jsonReturn, 'code', 200)) {
      deferred.resolveWith(this, [jsonReturn]);
    } 
    // Failure
    else {
      deferred.rejectWith(this, [jsonReturn]);
    }
  });

  return deferred;
}
_

延期をキャンセルしたい理由

リストのフィルターとして機能する入力フィールドがあり、入力が終了してから0.5秒後にリストが自動的に更新されます。 2つのajax呼び出しが同時に未解決になる可能性があるため、前の呼び出しをキャンセルして、2番目以降に返されないようにし、古いデータを表示する必要があります。

気に入らないソリューション

  • .fail()でアタッチされたハンドラーを起動するため、遅延オブジェクトを拒否したくありません。
  • Ajaxが戻ったときに自動的に解決または拒否のマークが付けられるため、無視することはできません。
  • 遅延オブジェクトを削除すると、ajax呼び出しが戻って、遅延オブジェクトを解決済みまたは拒否済みとしてマークしようとすると、エラーが発生します。

私は何をすべきか?

延期をキャンセルする方法、またはアタッチされているハンドラーを削除する方法はありますか?

デザインの修正方法に関するアドバイスは大歓迎ですが、ハンドラーを削除する方法やハンドラーの起動を防ぐ方法を見つけることが優先されます。

40
user879121

JQueryのドキュメントとコードを見ると、遅延されたjQueryをキャンセルする方法がわかりません。

代わりに、おそらくresolveWithハンドラーで、後続のajax呼び出しが既に発生しており、このajax呼び出しはその結果を無視する必要があることを知る方法が必要です。これは、グローバルに増加するカウンターで行うことができます。 ajax呼び出しの開始時に、カウンターを増分してから、値をローカル変数に取得するか、それをajaxオブジェクトのプロパティとして配置します。 resolveWithハンドラーで、ajax呼び出しの開始時と同じ値がカウンターに残っているかどうかを確認します。そうでない場合は、結果を無視します。その場合、新しいajax呼び出しは発生していないため、結果を処理できます。

あるいは、既に1つが飛行中のときに新しいajax呼び出しを実行することを拒否できるため、一度に複数の飛行を実行することはできません。 1つが終了したら、その結果を使用するか、必要に応じて次の結果を起動できます。

16
jfriend00

希望するように遅延オブジェクトを「キャンセル」することはできませんが、単純なクロージャーを作成して、jqXHRオブジェクトを返す$ .ajaxを介した最後のajax呼び出しを追跡できます。これを行うことで、最後のjqXHRが終了していない場合に新しいjqXHRが再生されたときに、呼び出しを単にabort()できます。コードの場合は、jqXHRを拒否し、遅延オブジェクトを開いたままにして、最初に希望どおりに削除します。

var api = (function() {
    var jqXHR = null;

    return function(options) {
        var url = options.url;

        if (jqXHR && jqXHR.state() === 'pending') {
            //Calls any error / fail callbacks of jqXHR
            jqXHR.abort();
        }

        var deferred = $.Deferred(function() {
            this.done(options.success);
            this.fail(options.error);
        });

        jqXHR = $.ajax({
             url: url,
             data: options.toSend,
             dataType: 'jsonp'
        });

        jqXHR.done(function(data, textStatus, jqXHR) {
            if (data.f && data.f !== "false") {
                deferred.resolve();
            } else {
                deferred.reject();
            }
        });

        //http://api.jquery.com/deferred.promise/  
        //keeps deferred's state from being changed outside this scope      
        return deferred.promise();
    };
})();

jsfiddle にこれを投稿しました。それをテストしたい場合。タイムアウト設定はjsfiddles遅延器と組み合わせて使用​​され、中断されるコールをシミュレートします。ログを表示するには、コンソール対応のブラウザが必要です。

余談ですが、.success()、. error()、complete()メソッドを遅延メソッドdone()、fail()、およびalways()に切り替えてください。 jquery/ajax 経由

非推奨の通知:jqXHR.success()、jqXHR.error()、およびjqXHR.complete()コールバックは、jQuery 1.8で非推奨になります。最終的な削除のためにコードを準備するには、代わりにjqXHR.done()、jqXHR.fail()、jqXHR.always()を使用してください。

7
Ryan Q

JustinY:あなたは本当にあなたが望むものにすでに近いようです。すでに2つの遅延オブジェクト(inner-> ajaxとouter-> $ .Deferred())を使用しています。次に、内部遅延を使用して、いくつかの条件に基づいて外部遅延を解決する方法を決定します。

まあ、そうしたくないときは、外側の遅延オブジェクトをまったく解決しないでください(おそらく、内部のdfdが解決/拒否できるようにするためのトグルゲートとして機能するブール変数があります)。悪いことは何も起こりません。この関数全体にアタッチしたハンドラは起動しません。内部成功関数の例:

if(gateOpen){
  gateOpen = false;
  if(hasStatus(jsonReturn, 'code', 200)) {
    deferred.resolveWith(this, [jsonReturn]);
  }
  else {
    deferred.rejectWith(this, [jsonReturn]);
  }
}

アプリケーションの他のいくつかのロジックは、gateOpenがtrueに戻されるタイミングを決定します(ある種の_.throttle()または_.debounce()タイムアウト、ユーザー操作など)。他の要求を追跡またはキャンセルしたい場合その関数の他の部分では、あなたもそれを行うことができます。しかし、基本的なことは、ORを解決する必要がないことです。外側の据え置きを拒否します。内側のスレッドをキャンセル/中止しなくても、それはキャンセルと同じです。

1
Dtipson

遅延オブジェクトとAjaxリクエストをキャンセルする機能をシームレスに追加するシムを作成しました。

つまり、遅延オブジェクトがキャンセルされると、解決/拒否は完全に無視され、stateは「キャンセル」されます。

JQuery.comによると、「オブジェクトが解決または拒否された状態になると、その状態のままになります」。したがって、遅延オブジェクトが解決または拒否されると、キャンセルの試行は無視されます。

(function () {
    originals = {
        deferred: $.Deferred,
        ajax: $.ajax
    };

    $.Deferred = function () {

        var dfr = originals.deferred(),
            cancel_dfr = originals.deferred();

        dfr.canceled = false;

        return {
            cancel: function () {
                if (dfr.state() == 'pending') {
                    dfr.canceled = true;
                    cancel_dfr.resolve.apply(this, arguments);
                }
                return this;
            },

            canceled: cancel_dfr.done,

            resolve: function () {
                if ( ! dfr.canceled) {
                    dfr.resolve.apply(dfr, arguments);
                    return this;
                }
            },

            resolveWith: function () {
                if ( ! dfr.canceled) {
                    dfr.resolveWith.apply(dfr, arguments);
                    return this;
                }
            },

            reject: function () {
                if ( ! dfr.canceled) {
                    dfr.reject.apply(dfr, arguments);
                    return this;
                }
            },

            rejectWith: function () {
                if ( ! dfr.canceled) {
                    dfr.rejectWith.apply(dfr, arguments);
                    return this;
                }
            },

            notify: function () {
                if ( ! dfr.canceled) {
                    dfr.notify.apply(dfr, arguments);
                    return this;
                }
            },

            notifyWith: function () {
                if ( ! dfr.canceled) {
                    dfr.notifyWith.apply(dfr, arguments);
                    return this;
                }
            },

            state: function () {
                if (dfr.canceled) {
                    return "canceled";
                } else {
                    return dfr.state();
                }
            },

            always   : dfr.always,
            then     : dfr.then,
            promise  : dfr.promise,
            pipe     : dfr.pipe,
            done     : dfr.done,
            fail     : dfr.fail,
            progress : dfr.progress
        };
    };


    $.ajax = function () {

        var dfr = $.Deferred(),
            ajax_call = originals.ajax.apply(this, arguments)
                .done(dfr.resolve)
                .fail(dfr.reject),

            newAjax = {},

            ajax_keys = [
                "getResponseHeader",
                "getAllResponseHeaders",
                "setRequestHeader",
                "overrideMimeType",
                "statusCode",
                "abort"
            ],

            dfr_keys = [
                "always",
                "pipe",
                "progress",
                "then",
                "cancel",
                "state",
                "fail",
                "promise",
                "done",
                "canceled"
            ];

        _.forEach(ajax_keys, function (key) {
            newAjax[key] = ajax_call[key];
        });

        _.forEach(dfr_keys, function (key) {
            newAjax[key] = dfr[key];
        });

        newAjax.success = dfr.done;
        newAjax.error = dfr.fail;
        newAjax.complete = dfr.always;

        Object.defineProperty(newAjax, 'readyState', {
            enumerable: true,
            get: function () {
                return ajax_call.readyState;
            },
            set: function (val) {
                ajax_call.readyState = val;
            }
        });

        Object.defineProperty(newAjax, 'status', {
            enumerable: true,
            get: function () {
                return ajax_call.status;
            },
            set: function (val) {
                ajax_call.status = val;
            }
        });

        Object.defineProperty(newAjax, 'statusText', {
            enumerable: true,
            get: function () {
                return ajax_call.statusText;
            },
            set: function (val) {
                ajax_call.statusText = val;
            }
        });

        // canceling an ajax request should also abort the call
        newAjax.canceled(ajax_call.abort);

        return newAjax;
    };
});

追加したら、ajax呼び出しをキャンセルできます。

var a = $.ajax({
        url: '//example.com/service/'
    });

a.cancel('the request was canceled');

// Now, any resolutions or rejections are ignored, and the network request is dropped.

..または単純な遅延オブジェクト:

var dfr = $.Deferred();

dfr
    .done(function () {
        console.log('Done!');
    })
    .fail(function () {
        console.log('Nope!');
    });

dfr.cancel(); // Now, the lines below are ignored. No console logs will appear.

dfr.resolve();
dfr.reject();
1
Joshua Hansen