2012-12-18 34 views
0

我正在致力於擴展jQuery的ajaxPrefilter接口以向AJAX調用添加其他功能;即在beforeSend()方法中使用setRequestHeader()將報頭數據添加到XHR請求中。事情是,ajaxPrefilter和ajax調用本身都可以包含beforeSend選項,其中一個或兩個都可以包含異步功能。如何正確使用jQuery延遲/承諾自動調用的函數,如.ajax()的beforeSend()?

假設我ajaxPrefilter看起來這樣:

(function($) { 
    $.ajaxPrefilter(function(options, originalOptions, jqXHR) { 
     var auth = $.cookie("fake_site"), 
      normalizedRequest = $.Deferred(); 

     if (originalOptions.authorizeUser) { 
      options.beforeSend = function() { 
       jqXHR.setRequestHeader("Authorization", "Session " + auth); 
      } 

      // resolving the jqXHR Deferred, statusCode handling, etc. 
     } 

    }); 
})(jQuery);​ 

現在,無論選項originalOptions可能包含beforeSend()方法,我想options.beforeSend來總是先執行。這似乎是一個總理候選人遞延/承諾用法,但由於事實beforeSend自動調用,我不能做到這一點:

options.beforeSend = function(jqXHR) { 
    // setting the request header; creating, resolving, and returning the Deferred 
} 

$.when(options.beforeSend(jqXHR)).then(function() { 
    originalOptions.beforeSend(); 
}); 

正如預期的那樣,這將導致options.beforeSend開火兩次;這是由於其定義聲明和$.when呼叫。

如何使用Deferreds/promise來管理自動調用的方法,如beforeSend

回答

1

關於控制對beforeSend回調後會發生什麼情況,只有兩種選擇:

false以外
  • 任何回報,允許繼續進行該項請求,或者
  • 回報false取消該請求。

你確實可以在回調中發出一個ajax請求,但無論返回什麼都會導致原始(外部)ajax請求立即發出或取消。由於回調將無條件地完成並返回,因此不可能通過延期或其他方式使外部ajax依賴於內部。

爲了達到您想要的效果,您可以在回調中發出級聯中的兩個Ajax請求(第二個依賴於第一個請求)並返回false以取消原始請求。有一天,你可能會想要採用這種模式,但如果原始請求被無條件地拋棄(通過返回false),可能會有一些不明確的理由,它只不過是一種實現兩個Ajax請求的複雜方式級聯,不涉及beforeSend,即:

$.ajax({ 
    //options 
}).done(function() { 
    $.ajax({ 
     //options 
    }).done(function(){ 
     //do stuff here 
    }); 
}); 

.pipe()等同。

總之,看起來不可能讓jQuery ajax請求依賴於在beforeSend回調中編碼的另一個異步操作,但是可以通過不涉及beforeSend的標準方法實現所需的效果。

0

以下是有點難看,但可以全局處理重新啓動原始請求,而不是嵌套每個請求。例如,如果您需要令牌並且令牌已過期,則此示例依賴全局api.getToken()函數返回的promise。

$.ajaxSetup({ 
    beforeSend: function(xhr,settings) { 
    var newTokenNeeded = false; 
    var originalSettings=settings; 

     var promise = api.getToken().then(function(data){ 

      if(newTokenNeeded && typeof originalSettings.nested ==='undefined'){ 
       originalSettings.nested=true;//this flag is used to prevent infinite loops 
       $.ajax(originalSettings);//here we re request the original, that we cancelled previously 
      }else{ 
       xhr.setRequestHeader('Authorization', 'Bearer ' +data.t); 
      } 
     }); 
     if(promise.state()==='pending'){ 
      newTokenNeeded=true; 
      return false; //returning false cancels the original request 
     } 
    } 

});