jquery

2011-12-23 6 views
32

का उपयोग करके AJAX कॉल को कैसे चेन करें I ब्राउज़र को लॉक किए बिना एन AJAX अनुरोधों की एक श्रृंखला बनाने की आवश्यकता है, और इसे पूरा करने के लिए jquery स्थगित ऑब्जेक्ट का उपयोग करना चाहते हैं।jquery

यहां तीन अनुरोधों के साथ एक सरलीकृत उदाहरण है, लेकिन मेरे प्रोग्राम को 100 से अधिक कतारबद्ध करने की आवश्यकता हो सकती है (ध्यान दें कि यह सटीक उपयोग केस नहीं है, वास्तविक कोड को चरण की सफलता सुनिश्चित करने की आवश्यकता है (एन -1)

:

$(document).ready(function(){ 

    var deferred = $.Deferred(); 

    var countries = ["US", "CA", "MX"]; 

    $.each(countries, function(index, country){ 

     deferred.pipe(getData(country)); 

    }); 

}); 

function getData(country){ 

    var data = { 
     "country": country 
    }; 


    console.log("Making request for [" + country + "]"); 

    return $.ajax({ 
     type: "POST", 
     url: "ajax.jsp", 
     data: data, 
     dataType: "JSON", 
     success: function(){ 
      console.log("Successful request for [" + country + "]"); 
     } 
    }); 

} 

यहाँ कंसोल में लिखा जाता है (सभी अनुरोधों को समानांतर में बने होते हैं और प्रतिक्रिया समय सीधे प्रत्येक देश के लिए डेटा का आकार के लिए आनुपातिक अपेक्षा के अनुरूप है: अगले कदम के लिए) निष्पादित करने से पहले

Making request for [US] 
Making request for [CA] 
Making request for [MX] 
Successful request for [MX] 
Successful request for [CA] 
Successful request for [US] 

मैं स्थगित वस्तु को कैसे प्राप्त कर सकता हूं मेरे लिए इन कतार? मैंने पाइप में बदलने की कोशिश की है लेकिन एक ही परिणाम प्राप्त करें।

यहाँ वांछित परिणाम है:

Making request for [US] 
Successful request for [US] 
Making request for [CA] 
Successful request for [CA] 
Making request for [MX] 
Successful request for [MX] 

संपादित करें:

मैं अनुरोध पैरामीटर स्टोर करने के लिए एक सरणी का उपयोग करने के सुझाव की सराहना करते हैं, लेकिन jQuery टाल वस्तु अनुरोध क़तार में क्षमता है और मैं वास्तव में यह जानना चाहता हूं कि इस सुविधा का पूर्ण क्षमता कैसे उपयोग करें।

:

when(request[0]).pipe(request[1]).pipe(request[2])... pipe(request[N]); 

हालांकि, मैं आदेश में प्रभावी रूप प्रत्येक ट्रेवर्सल का उपयोग करने के लिए एक समय में एक कदम पाइप में अनुरोध प्रदान करना चाहते हैं:

यह मैं क्या करने की कोशिश कर रहा हूँ प्रभावी रूप से है

deferred.pipe(request[0]); 
deferred.pipe(request[1]); 
deferred.pipe(request[2]); 

उत्तर

29

function DeferredAjax(opts) { 
    this.options=opts; 
    this.deferred=$.Deferred(); 
    this.country=opts.country; 
} 
DeferredAjax.prototype.invoke=function() { 
    var self=this, data={country:self.country}; 
    console.log("Making request for [" + self.country + "]"); 

    return $.ajax({ 
     type: "GET", 
     url: "wait.php", 
     data: data, 
     dataType: "JSON", 
     success: function(){ 
      console.log("Successful request for [" + self.country + "]"); 
      self.deferred.resolve(); 
     } 
    }); 
}; 
DeferredAjax.prototype.promise=function() { 
    return this.deferred.promise(); 
}; 


var countries = ["US", "CA", "MX"], startingpoint = $.Deferred(); 
startingpoint.resolve(); 

$.each(countries, function(ix, country) { 
    var da = new DeferredAjax({ 
     country: country 
    }); 
    $.when(startingpoint).then(function() { 
     da.invoke(); 
    }); 
    startingpoint= da; 
}); 

फिडल http://jsfiddle.net/7kuX9/1/

में थोड़ा और अधिक स्पष्ट रूप से, पिछले लाइनों

c1=new DeferredAjax({country:"US"}); 
c2=new DeferredAjax({country:"CA"}); 
c3=new DeferredAjax({country:"MX"}); 

$.when(c1).then(function() {c2.invoke();}); 
$.when(c2).then(function() {c3.invoke();}); 

लिखा जा सकता है पाइप के साथ एक कस्टम वस्तु के साथ

function fireRequest(country) { 
     return $.ajax({ 
      type: "GET", 
      url: "wait.php", 
      data: {country:country}, 
      dataType: "JSON", 
      success: function(){ 
       console.log("Successful request for [" + country + "]"); 
      } 
     }); 
} 

var countries=["US","CA","MX"], startingpoint=$.Deferred(); 
startingpoint.resolve(); 

$.each(countries,function(ix,country) { 
    startingpoint=startingpoint.pipe(function() { 
     console.log("Making request for [" + country + "]"); 
     return fireRequest(country); 
    }); 
}); 

http://jsfiddle.net/k8aUj/1/

संपादित करें: परिणाम खिड़की http://jsfiddle.net/k8aUj/3/

प्रत्येक पाइप कॉल एक नया वादा, जो अगले पाइप के लिए इस्तेमाल किया बदले में है रिटर्न में लॉग outputting एक बेला। ध्यान दें कि मैंने केवल Sccess फ़ंक्शन प्रदान किया है, विफलताओं के लिए एक समान कार्य प्रदान किया जाना चाहिए।

प्रत्येक समाधान में, अजाक्स कॉल को फ़ंक्शन में लपेटकर आवश्यक होने तक देरी हो जाती है और श्रृंखला बनाने के लिए सूची में प्रत्येक आइटम के लिए एक नया वादा बनाया जाता है।

मेरा मानना ​​है कि कस्टम ऑब्जेक्ट श्रृंखला में हेरफेर करने का एक आसान तरीका प्रदान करता है, लेकिन पाइप आपके स्वाद के अनुरूप बेहतर हो सकते हैं।

नोट: jQuery 1.8 के रूप में, deferred.pipe() हटाई गई है, deferred.then यह बदल देता है।

+0

यह उत्तर निश्चित रूप से काम करता है, मैं इसे सभी को पचाने की कोशिश कर रहा हूं क्योंकि यह काफी जटिल है।धन्यवाद! – Graham

+0

मुझे लगता है कि मैं इसे अब देख रहा हूं। मेरे मूल कोड और आपके बीच का मुख्य अंतर यह प्रतीत होता है कि आप प्रत्येक अनुरोध के लिए डिफर्ड ऑब्जेक्ट्स बना रहे हैं, जहां मेरा एक भी डिफर्ड का उपयोग करने का प्रयास कर रहा था। क्या मैं सही हूँ? – Graham

+1

आपके लिए कुछ विशिष्ट प्रश्न: (1) जब आप पहले से ही अजाक्स कॉल से वादे वापस कर रहे हैं तो आप स्पष्ट रूप से एक वादा क्यों वापस कर सकते हैं? (2) "यह" "आत्म" को क्यों मान रहे हैं? (3) आपने पाइप() का उपयोग क्यों नहीं किया जब वह देशी jquery queue function है? (4) जब हम प्रत्येक अनुरोध के लिए डिफर्ड ऑब्जेक्ट्स बना रहे हैं, तो जब मैं "कतार" में सैकड़ों अनुरोधों को खिलााना शुरू करता हूं तो स्मृति आवश्यकता क्या होती है? एक डिफरर्ड ऑब्जेक्ट कितना हल्का है? – Graham

4

मुझे बिल्कुल यकीन नहीं है कि आप ऐसा क्यों करना चाहते हैं, लेकिन उन सभी यूआरएल की एक सूची रखें जिन्हें आपको अनुरोध करने की आवश्यकता है, और अगले 0 से अनुरोध न करें जब तक कि आपके success फ़ंक्शन को कॉल न किया जाए। आईई, success सशर्त रूप से deferred पर अतिरिक्त कॉल करेगा।

+0

मैं ग्राहक गोपनीयता के कारणों के लिए यहां सटीक कोड की प्रतिलिपि नहीं बना सकता, लेकिन मेरे पास इन कॉलों को अनुक्रमिक रूप से श्रृंखलाबद्ध करने का बहुत अच्छा कारण है। क्या आपके समाधान को पूरे सरणी को GetData फ़ंक्शन में पास करने की आवश्यकता नहीं होगी? – Graham

+0

स्कोप श्रृंखला में 'getData' फ़ंक्शन के ऊपर किसी भी स्थान पर अधिक या कम, या अन्यथा उपलब्ध है। उदाहरण के लिए, एक बंद में अपने मूल 2 कोड ब्लॉक (अपने मूल प्रश्न में) दोनों हैं, सरणी के साथ एक तीसरे खंड के रूप में संयुक्त। आपको यह भी ट्रैक रखने की आवश्यकता होगी कि कौन से अनुरोध पहले ही किए गए थे - लेकिन आप तत्वों को पॉप-अप करके इसे संभाल सकते हैं जैसे आप अनुरोध करते हैं (इसे एक ढेर की तरह व्यवहार करना)। – ziesemer

+1

यह टेंगेंशियल है, लेकिन क्या आप यह भी देख सकते हैं कि आप क्यों नहीं देख सकते कि मैं AJAX अनुरोधों को कतार क्यों देना चाहता हूं? दो बहुत अच्छे उपयोग के मामले हैं जिनके बारे में मैं सोच सकता हूं: 1. सीमित करना कि सर्वर पर कितने एक साथ अनुरोध भेजे जाते हैं, और 2. अनुरोधों के बीच संभावित निर्भरताएं। – Graham

2

अद्यतन: deferred.pipe

यह कुछ ऐसा है पहले से ही jQuery एपीआई में प्रलेखित है के लिए कोड का एक बहुत कुछ है मान्य नहीं है। http://api.jquery.com/deferred.pipe/

आप सभी 100 किए जाने तक बस उन्हें पाइप कर सकते हैं।

या, मैंने एन कॉल करने के लिए कुछ लिखा, और किए गए सभी कॉल के डेटा के साथ एक समारोह को हल किया। नोट: यह डेटा को सुपर एक्सएचआर ऑब्जेक्ट नहीं देता है। https://gist.github.com/1219564

+0

उस समय मैंने मूल रूप से इस प्रश्न को पोस्ट किया था, वहां बहुत कम दस्तावेज उपलब्ध था। – Graham

4

मुझे पता है कि मुझे देर हो चुकी है, लेकिन मेरा मानना ​​है कि आपका मूल कोड अधिकतर ठीक है लेकिन इसमें दो (शायद तीन) समस्याएं हैं।

आपके getData(country) को तुरंत आपके पाइप के पैरामीटर को कोड करने के कारण कहा जा रहा है। आपके पास जिस तरह से है, getData() तुरंत निष्पादित कर रहा है और परिणाम (AJAX का वादा, लेकिन http अनुरोध तुरंत शुरू होता है) pipe() पर पैरामीटर के रूप में पारित किया जाता है। तो कॉलबैक फ़ंक्शन को पास करने के बजाय, आप एक ऑब्जेक्ट पास कर रहे हैं - जिसके कारण पाइप का नया स्थगित तुरंत हल हो जाता है।

मैं इसे

deferred.pipe(function() { return getData(country); }); 

अब यह एक कॉलबैक फ़ंक्शन कि जब आस्थगित पाइप के माता-पिता हल किया गया है बुलाया जाएगा है की जरूरत है लगता है। इस तरह कोडिंग दूसरी समस्या उठाएगा। GetData() s में से कोई भी निष्पादित नहीं होगा जब तक कि मास्टर स्थगित नहीं हो जाता है।

संभावित तीसरी समस्या यह हो सकती है कि चूंकि आपके सभी पाइप मास्टर द्वारा स्थगित किए जाएंगे, आपके पास वास्तव में कोई श्रृंखला नहीं है और मैं सोच रहा हूं कि यह किसी भी समय उन्हें सभी को निष्पादित कर सकता है या नहीं। दस्तावेज़ों का कहना है कि कॉलबैक क्रम में निष्पादित किए जाते हैं, लेकिन चूंकि आपका कॉलबैक एक वादा देता है और एसिंक चलाता है, इसलिए वे सभी अभी भी समानांतर में कुछ हद तक निष्पादित कर सकते हैं। jQuery 1.8 के रूप में आप .then बजाय .pipe उपयोग कर सकते हैं:

तो, मैं आप की तरह इस

var countries = ["US", "CA", "MX"]; 
var deferred = $.Deferred(); 
var promise = deferred.promise(); 

$.each(countries, function(index, country) { 
    promise = promise.pipe(function() { return getData(country); }); 
}); 

deferred.resolve(); 
+0

दिलचस्प, मुझे इसे आजमा देना होगा। मेरे पास वापसी मूल्यों में अनुक्रम को पकड़ने की आवश्यकता नहीं थी, लेकिन jquery में गहराई से गोता लगाने के लिए हमेशा अच्छा होता है। – Graham

+0

मुझे यकीन नहीं है कि आप वापसी मूल्यों में _capture अनुक्रम के साथ क्या संदर्भ दे रहे हैं। क्या आप विस्तारित कर सकते हैं? –

+0

यह वही है जिसे मैं पसंद करता हूं क्योंकि तब मैं इसे बाद में हल कर सकता हूं क्योंकि 'स्थगित' को एक विकृत वस्तु को ठंडा कर दिया जाता है लेकिन 'वादा' केवल एक वादा वस्तु है। उदाहरण के लिए, मैं बाद में वादा श्रृंखला में कुछ जोड़ सकता हूं और जब यह काम पूरा हो जाता है तो केवल इसे हल करना चाहते हैं। लेकिन स्वीकृत उत्तर अभी भी ठीक है अगर आप कॉलबैक को निष्पादित किए जाने के ठीक ठीक कर रहे हैं। यह अभी भी क्रम में है कि वे जोड़े गए हैं। – gillyspy

5

नोट कुछ चाहिए लगता है। .then फ़ंक्शन अब एक नया वादा देता है और .pipe बहिष्कृत है क्योंकि इसकी अब आवश्यकता नहीं है। वादे के बारे में अधिक जानकारी के लिए promises spec देखें, और jquery निर्भरता के बिना जावास्क्रिप्ट वादे की क्लीनर लाइब्रेरी के लिए q.js देखें।

countries.reduce(function(l, r){ 
    return l.then(function(){return getData(r)}); 
}, $.Deferred().resolve()); 

और यदि आप q का उपयोग करना चाहते हैं।js:

//create a closure for each call 
function getCountry(c){return function(){return getData(c)};} 
//fire the closures one by one 
//note: in Q, when(p1,f1) is the static version of p1.then(f1) 
countries.map(getCountry).reduce(Q.when, Q()); 

मूल जवाब:

फिर भी एक और पाइप; बेहोश दिल है, लेकिन थोड़ा और अधिक कॉम्पैक्ट के लिए नहीं:

countries.reduce(function(l, r){ 
    return l.pipe(function(){return getData(r)}); 
}, $.Deferred().resolve()); 

Reduce documentation शायद सबसे अच्छी जगह को समझने के ऊपर कोड काम करता है शुरू करने के लिए है। असल में, इसमें दो तर्क, कॉलबैक और प्रारंभिक मान होता है।

कॉलबैक सरणी के सभी तत्वों पर इसे लागू रूप से लागू किया जाता है, जहां इसका पहला तर्क पिछले पुनरावृत्ति के परिणाम को खिलाया जाता है, और दूसरा तर्क वर्तमान तत्व है। यह चाल यहां है कि getData()jquery deferred promise देता है, और पाइप सुनिश्चित करता है कि वर्तमान तत्व पर GetData को कॉल करने से पहले पिछले तत्व का getData पूरा हो गया है।

दूसरा तर्क $.Deferred().resolve() एक हल किए गए स्थगित मूल्य के लिए एक मुहावरे है। यह कॉलबैक निष्पादन के पहले पुनरावृत्ति को खिलाया जाता है, और यह सुनिश्चित करता है कि पहले तत्व पर GetData तुरंत कॉल किया जाता है।

+0

यदि आप तीन से अधिक पंक्तियों पर इस उत्तर को टिप्पणी, व्याख्या और प्रारूपित कर सकते हैं, तो मुझे लगता है कि यह एक बहुत अच्छा जवाब हो सकता है। बहुत से लोगों के लिए यदि हम सभी इस तरह की चीज को तोड़ना बहुत मुश्किल है, भले ही हमारे पास 'कमी' का उपयोग किया जा सके। – hippietrail

+0

ग्रेट एफपी समाधान, मुझे यह पसंद है –

2

मुझे jQuery कतारों के साथ सफलता मिली है।

$(function(){ 
    $.each(countries, function(i,country){ 
     $('body').queue(function() { 
     getData(country); 
     }); 
    }); 
}); 

var getData = function(country){ 
    $.ajax({ 
    url : 'ajax.jsp', 
    data : { country : country }, 
    type : 'post', 
    success : function() {       
     // Que up next ajax call 
     $('body').dequeue(); 
    }, 
    error : function(){ 
     $('body').clearQueue(); 
    } 
    }); 
}; 
+0

सरल कुशल, एक आकर्षण की तरह काम किया धन्यवाद – Sherlock