$ .When () के लिए Deferreds के एक सरणी में पास


447

यहाँ क्या हो रहा है की एक वंचित उदाहरण है: http://jsfiddle.net/adamjford/YNGcm/20/

HTML:

<a href="#">Click me!</a>
<div></div>

जावास्क्रिप्ट:

function getSomeDeferredStuff() {
    var deferreds = [];

    var i = 1;
    for (i = 1; i <= 10; i++) {
        var count = i;

        deferreds.push(
        $.post('/echo/html/', {
            html: "<p>Task #" + count + " complete.",
            delay: count
        }).success(function(data) {
            $("div").append(data);
        }));
    }

    return deferreds;
}

$(function() {
    $("a").click(function() {
        var deferreds = getSomeDeferredStuff();

        $.when(deferreds).done(function() {
            $("div").append("<p>All done!</p>");
        });
    });
});

मैं चाहता हूँ "सब किया!" सभी आस्थगित कार्यों के पूरा होने के बाद प्रदर्शित होने के लिए, लेकिन $.when()यह पता नहीं चलता है कि आस्थगित वस्तुओं की एक सरणी को कैसे संभालना है। "सब कुछ कर दिया!" पहली बार हो रहा है क्योंकि सरणी एक आस्थगित वस्तु नहीं है, इसलिए jQuery आगे बढ़ता है और यह मानता है कि यह अभी किया गया है।

मुझे पता है कि कोई भी फ़ंक्शन में ऑब्जेक्ट्स को पास कर सकता है, $.when(deferred1, deferred2, ..., deferredX)लेकिन यह अज्ञात है कि मैं जिस वास्तविक समस्या को हल करने की कोशिश कर रहा हूं, उसमें कितनी आस्थगित वस्तुएं होंगी।



नीचे एक बहुत पुराने प्रश्न के लिए एक नया, सरल, उत्तर जोड़ा गया। आपको एक ही परिणाम प्राप्त करने के लिए किसी सरणी या सभी का उपयोग करने की आवश्यकता नहीं है $.when.apply
कोडिंग

रोल बैक प्रश्न विषय, क्योंकि यह बहुत विशिष्ट था (यह सिर्फ एक AJAX समस्या नहीं है)
Alnitak

जवाबों:


732

किसी भी फ़ंक्शन के लिए मानों की एक सरणी को पास करने के लिए जो सामान्य रूप से उन्हें अलग पैरामीटर होने की उम्मीद करते हैं, उपयोग करते हैं Function.prototype.apply, इसलिए इस मामले में आपको ज़रूरत है:

$.when.apply($, my_array).then( ___ );

Http://jsfiddle.net/YNGcm/21/ देखें

ES6 में, आप इसके बजाय ... प्रसार ऑपरेटर का उपयोग कर सकते हैं :

$.when(...my_array).then( ___ );

या तो मामले में, चूंकि यह संभावना नहीं है कि आप पहले से जानते .thenहोंगे कि हैंडलर को कितने औपचारिक मापदंडों की आवश्यकता होगी, तो argumentsप्रत्येक वादे के परिणाम को प्राप्त करने के लिए हैंडलर को सरणी को संसाधित करने की आवश्यकता होगी ।


4
यह काम करता है, कमाल। :) मुझे आश्चर्य है कि मैं Google के माध्यम से इस तरह के एक साधारण परिवर्तन को समाप्त करने में सक्षम नहीं था!
adamjford

9
कि है, क्योंकि यह एक सामान्य विधि, के लिए नहीं विशिष्ट है $.when- f.apply(ctx, my_array)फोन करेगा fसाथ this == ctxऔर तर्क करने के लिए सेट सामग्री की my_array
अलनीतक

4
@Alnitak: मैं थोड़ा शर्मिंदा हूं कि मुझे उस तरीके के बारे में पता नहीं था, इस बात पर विचार करते हुए कि मैं अब तक जावास्क्रिप्ट कैसे लिख रहा हूं!
adamjford

5
एफडब्ल्यूआईडब्ल्यू, एली के उत्तर में एक इयरलर प्रश्न के उत्तर के साथ लिंक $बनाम nullपहले पैरामीटर को पढ़ने के रूप में पारित करने की चर्चा है । इस विशेष मामले में यह कोई फर्क नहीं पड़ता, हालांकि।
अलनीतक

4
@Alnitak: हां, लेकिन $टाइपिंग की तुलना में कम टाइपिंग है nullऔर आप सुरक्षित हैं जब $.whenकार्यान्वयन में बदलाव होता है (ऐसा नहीं है कि इस मामले में इसकी संभावना है लेकिन thisडिफ़ॉल्ट रूप से अपरिवर्तित क्यों न रखें )।
टॉमाज़ ज़िलिस्की

109

ऊपर दिए गए वर्कअराउंड (धन्यवाद!) ठीक से दिए गए ऑब्जेक्ट्स को हटाए जाने की समस्या को ठीक से संबोधित नहीं करते हैं resolve()क्योंकि jQuery कॉल done()और fail()कॉलबैक को अलग-अलग मापदंडों के साथ करता है, न कि कोई सरणी। इसका मतलब है कि हमें argumentsछद्म-सरणी का उपयोग करना है ताकि सभी स्थगित / अस्वीकार की गई वस्तुओं को आस्थगितों के सरणी से लौटाया जा सके, जो बदसूरत है:

$.when.apply($,deferreds).then(function() {
     var objects=arguments; // The array of resolved objects as a pseudo-array
     ...
};

चूंकि हम आस्थगित की एक सरणी में पारित हो गए हैं, इसलिए परिणामों की एक सरणी वापस प्राप्त करना अच्छा होगा। एक छद्म-सरणी के बजाय एक वास्तविक सरणी को वापस लेना अच्छा होगा ताकि हम जैसे तरीकों का उपयोग कर सकें Array.sort()

यहाँ एक समाधान से प्रेरित है when.js की when.all()विधि है कि पतों इन समस्याओं:

// Put somewhere in your scripting environment
if (typeof jQuery.when.all === 'undefined') {
    jQuery.when.all = function (deferreds) {
        return $.Deferred(function (def) {
            $.when.apply(jQuery, deferreds).then(
                function () {
                    def.resolveWith(this, [Array.prototype.slice.call(arguments)]);
                },
                function () {
                    def.rejectWith(this, [Array.prototype.slice.call(arguments)]);
                });
        });
    }
}

अब आप केवल आस्थगित / वादों के एक समूह में पास कर सकते हैं और अपने कॉलबैक में हल / अस्वीकृत वस्तुओं की एक सरणी वापस पा सकते हैं, जैसे:

$.when.all(deferreds).then(function(objects) {
    console.log("Resolved objects:", objects);
});

6
आप समाधान का उपयोग करना चाहते हो सकता है और अस्वीकार कर सकते हैं तो बस आप के रूप में एक ही मूल deferreds 'इस' deferred.resolveWith (यह, [Array.prototyp.slice.call) (तर्क)) आदि
जेमी Pate

1
आपके कोड के साथ बस एक छोटी सी समस्या है, जब सरणी में केवल एक तत्व होता है परिणाम सरणी केवल एक परिणाम के साथ एक सरणी के बजाय परिणाम देता है (जो किसी सरणी की अपेक्षा कोड को तोड़ देगा)। इसे ठीक करने के लिए, इस सुविधा का उपयोग var toArray = function (args) { return deferreds.length > 1 ? $.makeArray(args) : [args]; }करने के बजाय Array.prototype.slice.call
लुआन निको

हम्म, यह किसी भी 404 को पकड़ने के लिए प्रतीत नहीं होता है।
t.mikael.d

कारण मिला, .फेल को इसके बजाय .reject होना चाहिए - इसलिए यह 404 को पकड़ सकता है।
t.mikael.d

38

आप whenअपने ऐरे से विधि लागू कर सकते हैं :

var arr = [ /* Deferred objects */ ];

$.when.apply($, arr);

आप jQuery Deferreds की एक सरणी के साथ कैसे काम करते हैं?


मैंने वास्तव में उस प्रश्न को देखा था, लेकिन मुझे लगता है कि उस प्रश्न के सभी अतिरिक्त विवरणों ने मेरी समस्या का उत्तर दिया (जो कि वहां सही था) मेरे सिर के ऊपर से उड़ान भरने के लिए।
adamjford

1
@adamjford, यदि यह आपको कोई बेहतर महसूस कराता है, तो मुझे आपका प्रश्न उपभोग करने में आसान लगा (और इस सटीक मुद्दे के लिए मेरी विशेष Google खोज पर सबसे पहले)।
Patridge

@ दलित: यह सुनकर खुशी हुई कि इससे आपको मदद मिली!
adamjford

यह एक महान जवाब है, लेकिन यह मेरे लिए स्पष्ट नहीं था कि मूल प्रश्न में उदाहरण के लिए यह कैसे लागू होता है। लिंक किए गए प्रश्न से परामर्श करने के बाद, यह स्पष्ट हो गया कि लाइन "$ .when (deferreds) .done (फ़ंक्शन () {" को बस "$ .when.apply ($, deferreds) (.done (फ़ंक्शन) {) में बदला जाना चाहिए। "। सही?
माला पोप

7

कई समानांतर AJAX कॉल करते समय, आपके पास संबंधित प्रतिक्रियाओं को संभालने के लिए दो विकल्प होते हैं।

  1. एक या नहीं की सिफारिश के बाद सिंक्रोनस AJAX कॉल / एक का उपयोग करें
  2. Promises'सरणी का उपयोग करें और $.whenजो स्वीकार करता है promiseऔर उसके कॉलबैक .doneको कॉल किया जाता है जब सभी promiseएस संबंधित प्रतिक्रियाओं के साथ सफलतापूर्वक वापस आ जाते हैं।

उदाहरण

function ajaxRequest(capitalCity) {
   return $.ajax({
        url: 'https://restcountries.eu/rest/v1/capital/'+capitalCity,
        success: function(response) {
        },
        error: function(response) {
          console.log("Error")
        }
    });
}
$(function(){
   var capitalCities = ['Delhi', 'Beijing', 'Washington', 'Tokyo', 'London'];
   $('#capitals').text(capitalCities);

   function getCountryCapitals(){ //do multiple parallel ajax requests
      var promises = [];   
      for(var i=0,l=capitalCities.length; i<l; i++){
            var promise = ajaxRequest(capitalCities[i]);
            promises.push(promise);
      }
  
      $.when.apply($, promises)
        .done(fillCountryCapitals);
   }
  
   function fillCountryCapitals(){
        var countries = [];
        var responses = arguments;
        for(i in responses){
            console.dir(responses[i]);
            countries.push(responses[i][0][0].nativeName)
        }  
        $('#countries').text(countries);
   }
  
   getCountryCapitals()
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
  <h4>Capital Cities : </h4> <span id="capitals"></span>
  <h4>Respective Country's Native Names : </h4> <span id="countries"></span>
</div>


1
आपका उत्तर ओवररिएक्ट करता है, और इसलिए आपका शीर्षक प्रश्न शीर्षक में बदल गया। ओपी पहले से ही जानता था कि AJAX कॉल कैसे करें और आस्थगित वस्तुओं की एक सरणी प्राप्त करें। एकमात्र सवाल की बात कैसे है कि सरणी पारित किया गया था $.when
अलनीतक

5
मैंने सोचा कि उदाहरण के साथ विस्तार से व्याख्या करना बेहतर होगा, उपलब्ध विकल्पों के साथ। और इसके लिए मुझे नहीं लगता कि डाउनवोट आवश्यक था।
विनायकज

2
downvote 1 के लिए था। यहां तक ​​कि सिंक का सुझाव भी (यद्यपि एक सिफारिश के साथ नहीं) 2. उदाहरणों में खराब गुणवत्ता कोड ( for ... inएक सरणी पर सहित ?)
Alnitak

1
1. सहमत, 2. सहमत नहीं होना चाहिए था (not recommended)- for ... inठीक है क्योंकि सरणी में केवल वे गुण होते हैं जिनकी आवश्यकता होती है (कोई अतिरिक्त गुण नहीं)। thanx वैसे भी
विनायक

1
पुन: 2 - समस्या यह है कि यह अन्य लोगों द्वारा कॉपी किया जा सकता है जो कि गारंटी नहीं दे सकते हैं, या जोड़ने के लिए पर्याप्त गूंगा हो गए हैं Array.prototype। किसी भी घटना में, गैर-प्रदर्शन-महत्वपूर्ण कोड के लिए / लूप के .mapबजाय उपयोग करना बेहतर होगा , जैसे - काम किया। forpushvar promises = capitalCities.map(ajaxRequest); $.when.apply($, promises).then(fillCountryCapitals)
एल्निटैक

6

एक सरल विकल्प के रूप में, जिसकी आवश्यकता नहीं है $.when.applyया एक array, आप कई समानांतर वादों के लिए एक ही वादा करने के लिए निम्न पैटर्न का उपयोग कर सकते हैं:

promise = $.when(promise, anotherPromise);

जैसे

function GetSomeDeferredStuff() {
    // Start with an empty resolved promise (or undefined does the same!)
    var promise;
    var i = 1;
    for (i = 1; i <= 5; i++) {
        var count = i;

        promise = $.when(promise,
        $.ajax({
            type: "POST",
            url: '/echo/html/',
            data: {
                html: "<p>Task #" + count + " complete.",
                delay: count / 2
            },
            success: function (data) {
                $("div").append(data);
            }
        }));
    }
    return promise;
}

$(function () {
    $("a").click(function () {
        var promise = GetSomeDeferredStuff();
        promise.then(function () {
            $("div").append("<p>All done!</p>");
        });
    });
});

टिप्पणियाँ:

  • मैं किसी एक श्रृंखला के वादे क्रमिक रूप से देखने के बाद इसका उपयोग कर रहा था promise = promise.then(newpromise)
  • नकारात्मक पक्ष यह है कि दृश्यों के पीछे अतिरिक्त वादा ऑब्जेक्ट बनाता है और अंत में पारित किए गए कोई भी पैरामीटर बहुत उपयोगी नहीं हैं (क्योंकि वे अतिरिक्त स्तर के अंदर नेस्टेड हैं)। आप क्या चाहते हैं, हालांकि यह छोटा और सरल है।
  • उल्टा यह कोई सरणी या सरणी प्रबंधन की आवश्यकता है।

2
अगर मैं गलत हूं तो मुझे सुधारें, लेकिन आपका दृष्टिकोण प्रभावी रूप से $ .when ($ .when ($ .when (...))) को प्रभावी ढंग से घोंसले के शिकार कर रहा है, इसलिए यदि आप 10 पुनरावृत्तियों होते हैं, तो आप पुनरावर्ती नेस्टेड 10 स्तरों को समाप्त कर देते हैं। यह बहुत समानांतर नहीं लगता है क्योंकि आपको बच्चे के नेस्टेड वादे को वापस करने के लिए प्रत्येक स्तर के लिए इंतजार करना पड़ता है इससे पहले कि वह अपना वादा वापस कर सके - मुझे लगता है कि स्वीकृत उत्तर में सरणी दृष्टिकोण बहुत साफ है क्योंकि यह लचीले पैरामीटर व्यवहार में बनाया गया है $ .when () विधि।
एंथनी मैकलिन

@AnthonyMcLin: इसका उद्देश्य कोडिंग का एक सरल विकल्प प्रदान करना है, न कि बेहतर प्रदर्शन (जो कि अधिकांश Async कोडिंग के साथ नगण्य है), जैसा कि then()एक समान तरीके से कॉलिंग के साथ किया जाता है । इसके साथ व्यवहार करना $.whenहै क्योंकि यह समानांतर है (जंजीर नहीं है)। कृपया एक उपयोगी विकल्प को फेंकने से पहले इसे आज़माएं क्योंकि यह काम करता है :)
गॉन कोडिंग

2
@Alnitak: पाठ्यक्रमों के लिए घोड़े। आप निश्चित रूप से एक राय के हकदार हैं, लेकिन आपने स्पष्ट रूप से इसका उपयोग नहीं किया है। मेरी अपनी राय इस तकनीक के व्यावहारिक उपयोगों पर आधारित है। यह काम करता है और इसका उपयोग करता है, इसलिए क्यों "कैविट्स के भार" (एक) और "कुछ भी नहीं हल करता है" जैसे अतिरंजनाओं के आधार पर टूलबॉक्स से एक उपकरण को बाहर फेंक दें (यह सच नहीं है - यह सरणी प्रसंस्करण को समाप्त करता है और समानांतर वादों के पीछा को सरल बनाता है जहां वापसी मूल्यों की आवश्यकता नहीं है, जैसा कि आपको पता होना चाहिए कि शायद ही कभी समानांतर प्रसंस्करण मामलों में उपयोग किया जाता है)। डाउनवोट्स के लिए माना जाता है कि "यह उत्तर उपयोगी नहीं है" :)
कोडिंग

1
हाय @GoneCoding। क्या मैं पूछ सकता हूं कि आप अपने उत्तरों में वोटिंग कमेंट्री नहीं जोड़ते हैं? यह टिप्पणियों के लिए उपयुक्त है, लेकिन अन्यथा यह शोर है जो अन्यथा अच्छी सामग्री से विचलित करता है। धन्यवाद।
halfer

1
@ गोल्फर: मैं कोई और पोस्ट नहीं करता हूं, लेकिन कुछ भी मूल के लिए प्रदर्शित अज्ञानता पर नाराज हूं। आजकल अपने आप में सभी नए विचारों को रखते हुए :)
कोडिंग किया

4

मैं $ .each का उपयोग करके अन्य को प्रस्तावित करना चाहता हूं:

  1. हम अजाक्स समारोह की घोषणा कर सकते हैं जैसे:

    function ajaxFn(someData) {
        this.someData = someData;
        var that = this;
        return function () {
            var promise = $.Deferred();
            $.ajax({
                method: "POST",
                url: "url",
                data: that.someData,
                success: function(data) {
                    promise.resolve(data);
                },
                error: function(data) {
                    promise.reject(data);
                }
            })
            return promise;
        }
    }
  2. कोड का वह हिस्सा जहाँ हम भेजने के लिए ajax के साथ कार्यों की सरणी बनाते हैं:

    var arrayOfFn = [];
    for (var i = 0; i < someDataArray.length; i++) {
        var ajaxFnForArray = new ajaxFn(someDataArray[i]);
        arrayOfFn.push(ajaxFnForArray);
    }
  3. और अजाक्स भेजने के साथ कार्य करना:

    $.when(
        $.each(arrayOfFn, function(index, value) {
            value.call()
        })
    ).then(function() {
            alert("Cheer!");
        }
    )

1

यदि आप ट्रांसप्लान कर रहे हैं और ईएस 6 तक पहुंच है, तो आप फैलाने वाले सिंटैक्स का उपयोग कर सकते हैं जो विशेष रूप से एक असतत तर्क के रूप में किसी वस्तु के प्रत्येक चलने योग्य आइटम को लागू करता है, जिस तरह से इसकी $.when()आवश्यकता होती है।

$.when(...deferreds).done(() => {
    // do stuff
});

एमडीएन लिंक - फैला हुआ सिंटैक्स


0

यदि आप angularJS या Q वादा पुस्तकालय के कुछ प्रकार का उपयोग कर रहे हैं, तो आपके पास एक ऐसा .all()तरीका है जो इस सटीक समस्या को हल करता है।

var savePromises = [];
angular.forEach(models, function(model){
  savePromises.push(
    model.saveToServer()
  )
});

$q.all(savePromises).then(
  function success(results){...},
  function failed(results){...}
);

पूरा एपीआई देखें:

https://github.com/kriskowal/q/wiki/API-Reference#promiseall

https://docs.angularjs.org/api/ng/service/$q


4
यह पूरी तरह अप्रासंगिक है।
बेंजामिन ग्रुएनबाम

@BenjaminGruenbaum कैसे? सभी जावास्क्रिप्ट वादे पुस्तकालय एक समान एपीआई साझा करते हैं, और अलग-अलग कार्यान्वयन दिखाने में कुछ भी गलत नहीं है। मैं कोणीय के लिए एक उत्तर की तलाश में इस पृष्ठ पर पहुंच गया, और मुझे संदेह है कि कई अन्य उपयोगकर्ता इस पृष्ठ पर पहुंचेंगे और जरूरी नहीं कि यह केवल एक वातावरण में हो।
मास्टबलास्टा

2
अर्थात्, क्योंकि jQuery के वादे इस एपीआई को साझा नहीं करते हैं , यह स्टैक ओवरफ्लो पर एक उत्तर के रूप में पूरी तरह से अनुचित है - एंगुलर के लिए समान उत्तर हैं और आप वहां पूछ सकते हैं। (उल्लेख करने के लिए नहीं, आपको .mapयहां होना चाहिए लेकिन ओह अच्छी तरह से)।
बेंजामिन ग्रुएनबाम 14

0

मेरे पास एक बहुत समान मामला था जहां मैं प्रत्येक लूप में पोस्ट कर रहा था और फिर ajax से प्राप्त संख्याओं से कुछ क्षेत्रों में html मार्कअप सेट कर रहा था। मुझे तब इन फ़ील्ड्स और कुल फ़ील्ड में जगह के (अब-अद्यतन) मूल्यों का योग करने की आवश्यकता थी।

इस प्रकार समस्या यह थी कि मैं सभी नंबरों पर एक योग करने की कोशिश कर रहा था, लेकिन एसिंक्स अजाक्स कॉल से अभी तक कोई डेटा वापस नहीं आया था। कोड का पुन: उपयोग करने में सक्षम होने के लिए मुझे कुछ कार्यों में इस कार्यक्षमता को पूरा करने की आवश्यकता है। मेरा बाहरी कार्य इससे पहले कि मैं पूरी तरह से अपडेट किए गए DOM के साथ कुछ सामान ले जाऊं और डेटा का इंतजार करूं।

    // 1st
    function Outer() {
        var deferreds = GetAllData();

        $.when.apply($, deferreds).done(function () {
            // now you can do whatever you want with the updated page
        });
    }

    // 2nd
    function GetAllData() {
        var deferreds = [];
        $('.calculatedField').each(function (data) {
            deferreds.push(GetIndividualData($(this)));
        });
        return deferreds;
    }

    // 3rd
    function GetIndividualData(item) {
        var def = new $.Deferred();
        $.post('@Url.Action("GetData")', function (data) {
            item.html(data.valueFromAjax);
            def.resolve(data);
        });
        return def;
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.