วิธีใดดีที่สุดในการลองคำขอ AJAX ใหม่เมื่อเกิดความล้มเหลวโดยใช้ jQuery


108

รหัสหลอก:

$(document).ajaxError(function(e, xhr, options, error) {
  xhr.retry()
})

ที่ดีไปกว่านั้นคือการถอยออกมา


1
ฉันไม่แน่ใจว่านี่เป็นวิธีที่ดีที่สุดหรือเปล่าดังนั้นแค่แสดงความคิดเห็น แต่ถ้าคุณเรียก ajax จาก fucntion คุณสามารถให้พารามิเตอร์triesได้และเมื่อคุณเรียกใช้ฟังก์ชันของคุณtries+1ไม่สำเร็จ หยุดการดำเนินการบนtries==3หรือหมายเลขอื่น ๆ
แนน


ทางออกที่ดีที่นี่: stackoverflow.com/questions/6298612/…
Jeremy A. West

คำตอบ:


240

สิ่งนี้:


$.ajax({
    url : 'someurl',
    type : 'POST',
    data :  ....,   
    tryCount : 0,
    retryLimit : 3,
    success : function(json) {
        //do something
    },
    error : function(xhr, textStatus, errorThrown ) {
        if (textStatus == 'timeout') {
            this.tryCount++;
            if (this.tryCount <= this.retryLimit) {
                //try again
                $.ajax(this);
                return;
            }            
            return;
        }
        if (xhr.status == 500) {
            //handle error
        } else {
            //handle error
        }
    }
});

12
ฉันได้ใช้โซลูชันของ @ Sudhir และสร้างปลั๊กอิน $ .retryAjax บน github ที่นี่: github.com/mberkom/jQuery.retryAjax
Michael Berkompas

2
นี่ใช้ไม่ได้สำหรับฉัน this.tryCount ในเงื่อนไขเป็น 1 เสมอ
user304602

2
@MichaelBerkompas - ปลั๊กอินของคุณยังใช้งานได้หรือไม่ ไม่ได้รับคอมมิชชั่นภายใน 2 ปี
Hendrik

2
สิ่งนี้จะใช้งานได้หรือไม่หากตัวจัดการการโทรกลับอื่นเช่น.successแนบกับการเรียกฟังก์ชันที่ส่งคืนคำขอ ajax นี้
ProblemsOfSumit

17
คู่ของtryCountและretryLimitมากเกินไป พิจารณาใช้ตัวแปรเพียง 1 ตัว:this.retryLimit--; if (this.retryLimit) { ... $.ajax(this) ... }
vladkras

15

แนวทางหนึ่งคือการใช้ฟังก์ชัน wrapper:

(function runAjax(retries, delay){
  delay = delay || 1000;
  $.ajax({
    type        : 'GET',
    url         : '',
    dataType    : 'json',
    contentType : 'application/json'
  })
  .fail(function(){
    console.log(retries); // prrint retry count
    retries > 0 && setTimeout(function(){
        runAjax(--retries);
    },delay);
  })
})(3, 100);

อีกวิธีหนึ่งคือการใช้retriesคุณสมบัติบน$.ajax

// define ajax settings
var ajaxSettings = {
  type        : 'GET',
  url         : '',
  dataType    : 'json',
  contentType : 'application/json',
  retries     : 3  //                 <-----------------------
};

// run initial ajax
$.ajax(ajaxSettings).fail(onFail)

// on fail, retry by creating a new Ajax deferred
function onFail(){
  if( ajaxSettings.retries-- > 0 )
    setTimeout(function(){
        $.ajax(ajaxSettings).fail(onFail);
    }, 1000);
}

อีกวิธีหนึ่ง ( GIST ) - แทนที่ต้นฉบับ$.ajax(ดีกว่าสำหรับ DRY)

// enhance the original "$.ajax" with a retry mechanism 
$.ajax = (($oldAjax) => {
  // on fail, retry by creating a new Ajax deferred
  function check(a,b,c){
    var shouldRetry = b != 'success' && b != 'parsererror';
    if( shouldRetry && --this.retries > 0 )
      setTimeout(() => { $.ajax(this) }, this.retryInterval || 100);
  }

  return settings => $oldAjax(settings).always(check)
})($.ajax);



// now we can use the "retries" property if we need to retry on fail
$.ajax({
    type          : 'GET',
    url           : 'http://www.whatever123.gov',
    timeout       : 2000,
    retries       : 3,     //       <-------- Optional
    retryInterval : 2000   //       <-------- Optional
})
// Problem: "fail" will only be called once, and not for each retry
.fail(()=>{
  console.log('failed') 
});

จุดที่จะต้องพิจารณาคือการทำให้แน่ใจว่า$.ajaxวิธีการไม่ได้ถูกห่อไว้แล้วก่อนหน้านี้ในการสั่งซื้อเพื่อหลีกเลี่ยงรหัสเดียวกันทำงานสองครั้ง


คุณสามารถคัดลอกและวางข้อมูลโค้ดเหล่านี้ (ตามสภาพ) ลงในคอนโซลเพื่อทดสอบได้


ขอบคุณสำหรับสคริปต์ มันทำงานร่วมกับ $ .ajaxSetup หรือไม่
Sevban Öztürk

@ SevbanÖztürk - คุณหมายถึงอะไร? แค่ลอง :)
vsync

ขอบคุณที่สอนวิธีจัดโครงสร้างเสื้อคลุม! สิ่งนี้ดีกว่าการออกแบบฟังก์ชันเรียกซ้ำแบบเก่าที่ฉันเคยใช้
decoder7283

7

ฉันประสบความสำเร็จมากมายกับโค้ดด้านล่างนี้ (ตัวอย่าง: http://jsfiddle.net/uZSFK/ )

$.ajaxSetup({
    timeout: 3000, 
    retryAfter:7000
});

function func( param ){
    $.ajax( 'http://www.example.com/' )
        .success( function() {
            console.log( 'Ajax request worked' );
        })
        .error(function() {
            console.log( 'Ajax request failed...' );
            setTimeout ( function(){ func( param ) }, $.ajaxSetup().retryAfter );
        });
}

4
การเปลี่ยนแปลงเดียวที่ฉันขอแนะนำคือการแทนที่ 'func ("' + param" '")' ด้วยฟังก์ชัน () {func (param)} ด้วยวิธีนี้คุณสามารถส่งผ่านพารามิเตอร์ได้โดยตรงโดยไม่ต้องแปลงเป็นสตริงและย้อนกลับ ซึ่งอาจล้มเหลวได้ง่ายมาก!
fabspro

@fabspro เสร็จแล้ว. ขอบคุณ!
Nabil Kadimi

7
นี่ไม่ใช่การวนซ้ำไม่รู้จบหรือ? เนื่องจากคำถามมี retryLimit และเห็นได้ชัดว่าต้องการรองรับเซิร์ฟเวอร์ที่ไม่มีวันกลับมา ... ฉันคิดว่าสิ่งนี้จะต้องอยู่ในนั้นจริงๆ
PandaWood

3
jQuery.ajaxSetup () คำอธิบาย: ตั้งค่าเริ่มต้นสำหรับคำขอ Ajax ในอนาคต ไม่แนะนำให้ใช้ api.jquery.com/jQuery.ajaxSetup
ร้องไห้สะอึกสะอื้น

2

คำตอบเหล่านี้.done()จะไม่ได้ผลหากมีใครโทรมาหลังจากโทรกลับโดยไม่ได้รับอนุญาตเพราะคุณจะไม่มีวิธีการที่จะเชื่อมต่อกับการโทรกลับในอนาคต ดังนั้นหากมีใครทำสิ่งนี้:

$.ajax({...someoptions...}).done(mySuccessFunc);

จากนั้นmySuccessFuncจะไม่ถูกเรียกให้ลองใหม่ นี่เป็นวิธีการแก้ปัญหาของเราซึ่งยืมมาอย่างหนักจากคำตอบของ @ cjpak ที่นี่ ในกรณีของฉันฉันต้องการลองอีกครั้งเมื่อ API Gateway ของ AWS ตอบสนองด้วยข้อผิดพลาด 502

const RETRY_WAIT = [10 * 1000, 5 * 1000, 2 * 1000];

// This is what tells JQuery to retry $.ajax requests
// Ideas for this borrowed from https://stackoverflow.com/a/12446363/491553
$.ajaxPrefilter(function(opts, originalOpts, jqXHR) {
  if(opts.retryCount === undefined) {
    opts.retryCount = 3;
  }

  // Our own deferred object to handle done/fail callbacks
  let dfd = $.Deferred();

  // If the request works, return normally
  jqXHR.done(dfd.resolve);

  // If the request fails, retry a few times, yet still resolve
  jqXHR.fail((xhr, textStatus, errorThrown) => {
    console.log("Caught error: " + JSON.stringify(xhr) + ", textStatus: " + textStatus + ", errorThrown: " + errorThrown);
    if (xhr && xhr.readyState === 0 && xhr.status === 0 && xhr.statusText === "error") {
      // API Gateway gave up.  Let's retry.
      if (opts.retryCount-- > 0) {
        let retryWait = RETRY_WAIT[opts.retryCount];
        console.log("Retrying after waiting " + retryWait + " ms...");
        setTimeout(() => {
          // Retry with a copied originalOpts with retryCount.
          let newOpts = $.extend({}, originalOpts, {
            retryCount: opts.retryCount
          });
          $.ajax(newOpts).done(dfd.resolve);
        }, retryWait);
      } else {
        alert("Cannot reach the server.  Please check your internet connection and then try again.");
      }
    } else {
      defaultFailFunction(xhr, textStatus, errorThrown); // or you could call dfd.reject if your users call $.ajax().fail()
    }
  });

  // NOW override the jqXHR's promise functions with our deferred
  return dfd.promise(jqXHR);
});

ข้อมูลโค้ดนี้จะย้อนกลับและลองใหม่หลังจากนั้น 2 วินาทีจากนั้น 5 วินาทีจากนั้น 10 วินาทีซึ่งคุณสามารถแก้ไขได้โดยการแก้ไขค่าคงที่ RETRY_WAIT

ฝ่ายสนับสนุนของ AWS แนะนำให้เราเพิ่มการลองใหม่เนื่องจากจะเกิดขึ้นกับเราเพียงครั้งเดียวในดวงจันทร์สีน้ำเงิน


ฉันพบว่าสิ่งนี้มีประโยชน์มากที่สุดสำหรับคำตอบทั้งหมดจนถึงตอนนี้ อย่างไรก็ตามบรรทัดสุดท้ายป้องกันการคอมไพล์ใน TypeScript ฉันไม่คิดว่าคุณควรจะส่งคืนอะไรจากฟังก์ชันนี้
Freddie

0

นี่คือปลั๊กอินขนาดเล็กสำหรับสิ่งนี้:

https://github.com/execjosh/jquery-ajax-retry

การหมดเวลาเพิ่มอัตโนมัติจะเป็นส่วนเสริมที่ดี

หากต้องการใช้งานทั่วโลกเพียงแค่สร้างฟังก์ชันของคุณเองด้วยลายเซ็น $ .ajax ใช้ที่นั่นลอง api อีกครั้งและแทนที่การเรียก $ .ajax ทั้งหมดของคุณด้วยฟังก์ชันใหม่ของคุณ

นอกจากนี้คุณสามารถแทนที่ $ .ajax ได้โดยตรง แต่คุณจะไม่สามารถโทร xhr ได้หากไม่ลองอีกครั้ง


0

นี่คือวิธีการที่เหมาะกับฉันสำหรับการโหลดไลบรารีแบบอะซิงโครนัส:

var jqOnError = function(xhr, textStatus, errorThrown ) {
    if (typeof this.tryCount !== "number") {
      this.tryCount = 1;
    }
    if (textStatus === 'timeout') {
      if (this.tryCount < 3) {  /* hardcoded number */
        this.tryCount++;
        //try again
        $.ajax(this);
        return;
      }
      return;
    }
    if (xhr.status === 500) {
        //handle error
    } else {
        //handle error
    }
};

jQuery.loadScript = function (name, url, callback) {
  if(jQuery[name]){
    callback;
  } else {
    jQuery.ajax({
      name: name,
      url: url,
      dataType: 'script',
      success: callback,
      async: true,
      timeout: 5000, /* hardcoded number (5 sec) */
      error : jqOnError
    });
  }
}

จากนั้นเพียงโทร.load_scriptจากแอพของคุณและซ้อนการโทรกลับที่ประสบความสำเร็จของคุณ:

$.loadScript('maps', '//maps.google.com/maps/api/js?v=3.23&libraries=geometry&libraries=places&language=&hl=&region=', function(){
    initialize_map();
    loadListeners();
});

0

คำตอบของ DemoUsers ใช้ไม่ได้กับ Zepto เนื่องจากสิ่งนี้ในฟังก์ชันข้อผิดพลาดชี้ไปที่ Window (และวิธีการใช้ 'this' นั้นไม่ปลอดภัยเพียงพอเนื่องจากคุณไม่รู้ว่าพวกเขาใช้ ajax อย่างไรหรือไม่จำเป็นต้องทำ)

สำหรับ Zepto บางทีคุณอาจลองด้านล่างจนถึงตอนนี้มันก็ใช้ได้ดีสำหรับฉัน:

var AjaxRetry = function(retryLimit) {
  this.retryLimit = typeof retryLimit === 'number' ? retryLimit : 0;
  this.tryCount = 0;
  this.params = null;
};
AjaxRetry.prototype.request = function(params, errorCallback) {
  this.tryCount = 0;
  var self = this;
  params.error = function(xhr, textStatus, error) {
    if (textStatus === 'timeout') {
      self.tryCount ++;
      if (self.tryCount <= self.retryLimit) {
        $.ajax(self.params)      
        return;
      }
    }
    errorCallback && errorCallback(xhr, textStatus, error);
  };
  this.params = params;
  $.ajax(this.params);
};
//send an ajax request
new AjaxRetry(2).request(params, function(){});

ใช้ตัวสร้างเพื่อให้แน่ใจว่ามีการส่งคำขออีกครั้ง!


0

โค้ดของคุณใกล้เต็มแล้ว :)

const counter = 0;
$(document).ajaxSuccess(function ( event, xhr, settings ) {
    counter = 0;
}).ajaxError(function ( event, jqxhr, settings, thrownError ) {
    if (counter === 0 /*any thing else you want to check ie && jqxhr.status === 401*/) {
        ++counter;
        $.ajax(settings);
    }
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.