मैं एसिंक्रोनस कॉल से प्रतिक्रिया कैसे लौटाऊं?


5506

मेरे पास एक फ़ंक्शन है fooजो अजाक्स अनुरोध करता है। मैं कैसे प्रतिक्रिया वापस कर सकता हूं foo?

मैंने successकॉलबैक से मान लौटाने की कोशिश की , साथ ही फ़ंक्शन के अंदर एक स्थानीय चर की प्रतिक्रिया को असाइन करने और उस एक को वापस करने की, लेकिन उन तरीकों में से कोई भी वास्तव में प्रतिक्रिया वापस नहीं करता है।

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            result = response;
            // return response; // <- I tried that one as well
        }
    });

    return result;
}

var result = foo(); // It always ends up being `undefined`.

जवाबों:


5700

→ विभिन्न उदाहरणों के साथ async व्यवहार की अधिक सामान्य व्याख्या के लिए, कृपया देखें कि किसी फ़ंक्शन के अंदर इसे संशोधित करने के बाद मेरा वैरिएबल अनलक्ड क्यों है? - एसिंक्रोनस कोड संदर्भ

→ यदि आप पहले से ही समस्या को समझते हैं, तो नीचे दिए गए संभावित समाधानों पर जाएं।

समस्या

एक में अजाक्स के लिए खड़ा है अतुल्यकालिक । इसका मतलब है कि अनुरोध भेजना (या प्रतिक्रिया प्राप्त करना) सामान्य निष्पादन प्रवाह से बाहर ले जाया जाता है। आपके उदाहरण में, $.ajaxतुरंत और अगले स्टेटमेंट को, return result;उस फ़ंक्शन से पहले निष्पादित किया जाता है जिसे आपने successकॉलबैक के रूप में पारित किया था।

यहाँ एक सादृश्य है जो उम्मीद है कि तुल्यकालिक और अतुल्यकालिक प्रवाह के बीच अंतर स्पष्ट करता है:

एक समय का

कल्पना कीजिए कि आप किसी मित्र को फोन करते हैं और उसे आपसे कुछ देखने के लिए कहते हैं। हालाँकि इसमें कुछ समय लग सकता है, आप फोन पर प्रतीक्षा करते हैं और अंतरिक्ष में घूरते हैं, जब तक कि आपका मित्र आपको वह उत्तर नहीं देता है जिसकी आपको आवश्यकता थी।

जब आप "सामान्य" कोड वाले फ़ंक्शन कॉल करते हैं तो वही हो रहा है:

function findItem() {
    var item;
    while(item_not_found) {
        // search
    }
    return item;
}

var item = findItem();

// Do something with item
doSomethingElse();

यद्यपि findItemनिष्पादन में लंबा समय लग सकता है, फिर भी आने वाले किसी भी कोड var item = findItem();को तब तक इंतजार करना पड़ता है जब तक कि फ़ंक्शन परिणाम नहीं देता।

अतुल्यकालिक

आप अपने मित्र को फिर से उसी कारण से बुलाते हैं। लेकिन इस बार आप उसे बताएं कि आप जल्दी में हैं और उसे आपको अपने मोबाइल फोन पर कॉल करना चाहिए । आप लटकाते हैं, घर छोड़ देते हैं और जो कुछ भी करने की आपने योजना बनाई है। एक बार जब आपका दोस्त आपको वापस बुलाता है, तो आप उसे दी गई जानकारी से निपट रहे हैं।

जब आप अजाक्स अनुरोध करते हैं तो वास्तव में यही होता है।

findItem(function(item) {
    // Do something with item
});
doSomethingElse();

प्रतिक्रिया की प्रतीक्षा करने के बजाय, निष्पादन तुरंत जारी रहता है और अजाक्स कॉल के निष्पादित होने के बाद बयान जारी होता है। अंततः प्रतिक्रिया प्राप्त करने के लिए, आप एक कॉल प्रदान करते हैं जिसे प्रतिक्रिया प्राप्त होने के बाद कॉल किया जाता है, कॉलबैक (कुछ नोटिस करें ? वापस कॉल करें )। उस कॉल के बाद आने वाले किसी भी स्टेटमेंट को कॉलबैक कहा जाता है।


समाधान)

जावास्क्रिप्ट की अतुल्यकालिक प्रकृति को गले लगाओ! हालांकि कुछ अतुल्यकालिक संचालन तुल्यकालिक समकक्ष प्रदान करते हैं (इसलिए "अजाक्स"), यह आमतौर पर उन्हें उपयोग करने के लिए हतोत्साहित किया जाता है, खासकर एक ब्राउज़र संदर्भ में।

यह क्यों बुरा है आप पूछें?

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

यह सब वास्तव में बुरा उपयोगकर्ता अनुभव है। उपयोगकर्ता यह बताने में सक्षम नहीं होगा कि सब कुछ ठीक काम कर रहा है या नहीं। इसके अलावा, प्रभाव धीमे कनेक्शन वाले उपयोगकर्ताओं के लिए और भी बुरा होगा।

निम्नलिखित में हम तीन अलग-अलग समाधानों को देखेंगे जो सभी एक-दूसरे के शीर्ष पर बन रहे हैं:

  • async/awaitयदि आप ट्रांसपिलर या रीजनरेटर का उपयोग करते हैं तो पुराने ब्राउज़रों में उपलब्ध (ES2017 +) के साथ वादा करता है
  • कॉलबैक (नोड में लोकप्रिय)
  • वादाthen() (ES2015 +, पुराने ब्राउज़रों में उपलब्ध यदि आप कई वादे पुस्तकालयों में से एक का उपयोग करते हैं)

सभी तीन वर्तमान ब्राउज़रों में उपलब्ध हैं, और नोड 7+।


ES2017 +: के साथ वादा करता हूँ async/await

2017 में जारी ECMAScript संस्करण ने अतुल्यकालिक कार्यों के लिए सिंटैक्स-स्तरीय समर्थन पेश किया । की मदद से asyncऔर await, आप एसिंक्रोनस को "सिंक्रोनस स्टाइल" में लिख सकते हैं। कोड अभी भी अतुल्यकालिक है, लेकिन इसे पढ़ना / समझना आसान है।

async/awaitवादों के शीर्ष पर बनाता है: एक asyncफ़ंक्शन हमेशा एक वादा वापस करता है। awaitवादे को उजागर नहीं करता है और या तो वादे के साथ वादे को हल किया गया था या यदि वादा खारिज कर दिया गया था, तो त्रुटि हो सकती है।

महत्वपूर्ण: आप केवल awaitएक asyncफ़ंक्शन के अंदर उपयोग कर सकते हैं । अभी, शीर्ष-स्तर awaitका समर्थन नहीं किया गया है, इसलिए आपको एक संदर्भ शुरू करने के लिए एक async IIFE ( तत्काल इनवॉइस फंक्शन एक्सप्रेशन ) करना पड़ सकता है async

आप MDN के बारे में asyncऔर अधिक पढ़ सकते हैं await

यहाँ एक उदाहरण है जो ऊपर देरी के शीर्ष पर बनाता है:

// Using 'superagent' which will return a promise.
var superagent = require('superagent')

// This is isn't declared as `async` because it already returns a promise
function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}


async function getAllBooks() {
  try {
    // GET a list of book IDs of the current user
    var bookIDs = await superagent.get('/user/books');
    // wait for 3 seconds (just for the sake of this example)
    await delay();
    // GET information about each book
    return await superagent.get('/books/ids='+JSON.stringify(bookIDs));
  } catch(error) {
    // If any of the awaited promises was rejected, this catch block
    // would catch the rejection reason
    return null;
  }
}

// Start an IIFE to use `await` at the top level
(async function(){
  let books = await getAllBooks();
  console.log(books);
})();

वर्तमान ब्राउज़र और नोड संस्करण समर्थन करते हैं async/await। आप पुनर्जन्मकर्ता (या ऐसे उपकरण जो पुनर्योजी का उपयोग करते हैं, जैसे बेबल ) का उपयोग करके अपने कोड को ES5 में बदलकर पुराने वातावरण का समर्थन कर सकते हैं ।


कार्य कॉलबैक स्वीकार करते हैं

कॉलबैक बस एक फ़ंक्शन है जो किसी अन्य फ़ंक्शन को दिया जाता है। जब भी यह तैयार होता है तो अन्य फ़ंक्शन कॉल किए गए फ़ंक्शन को कॉल कर सकते हैं। एक अतुल्यकालिक प्रक्रिया के संदर्भ में, जब भी अतुल्यकालिक प्रक्रिया की जाती है तो कॉलबैक कहा जाएगा। आमतौर पर, परिणाम कॉलबैक को पास किया जाता है।

प्रश्न के उदाहरण में, आप fooकॉलबैक स्वीकार कर सकते हैं और successकॉलबैक के रूप में इसका उपयोग कर सकते हैं । तो यह

var result = foo();
// Code that depends on 'result'

हो जाता है

foo(function(result) {
    // Code that depends on 'result'
});

यहां हमने फ़ंक्शन "इनलाइन" को परिभाषित किया है, लेकिन आप किसी भी फ़ंक्शन संदर्भ को पास कर सकते हैं:

function myCallback(result) {
    // Code that depends on 'result'
}

foo(myCallback);

foo स्वयं को इस प्रकार परिभाषित किया गया है:

function foo(callback) {
    $.ajax({
        // ...
        success: callback
    });
}

callbackउस फ़ंक्शन को संदर्भित करेगा जिसे हम पास करते हैं fooजब हम इसे कॉल करते हैं और हम बस इसे पास करते हैं success। यानी एक बार अजाक्स का अनुरोध सफल होने के बाद, कॉलबैक पर प्रतिक्रिया को $.ajaxकॉल callbackऔर पास करेगा (जिसे इसके साथ संदर्भित किया जा सकता है result, क्योंकि यह कॉलबैक को परिभाषित करता है)।

आप इसे कॉलबैक में भेजने से पहले प्रतिक्रिया की प्रक्रिया भी कर सकते हैं:

function foo(callback) {
    $.ajax({
        // ...
        success: function(response) {
            // For example, filter the response
            callback(filtered_response);
        }
    });
}

कॉलबैक का उपयोग करके कोड लिखना आसान हो सकता है जितना कि यह लग सकता है। सब के बाद, ब्राउज़र में जावास्क्रिप्ट भारी घटना संचालित (डोम ईवेंट) है। अजाक्स प्रतिक्रिया प्राप्त करना एक घटना के अलावा और कुछ नहीं है।
जब आपको थर्ड-पार्टी कोड के साथ काम करना पड़ता है, तो कठिनाइयाँ आ सकती हैं, लेकिन ज्यादातर समस्याओं को केवल एप्लिकेशन प्रवाह के माध्यम से सोचकर हल किया जा सकता है।


ES2015 +: तब के साथ वादा ()

वादा एपीआई ECMAScript 6 (ES2015) की एक नई सुविधा है, लेकिन यह अच्छा है ब्राउज़र समर्थन पहले से ही। कई पुस्तकालय भी हैं जो मानक वादे एपीआई को लागू करते हैं और अतुल्यकालिक कार्यों (जैसे ब्लूबर्ड ) के उपयोग और संरचना को आसान बनाने के लिए अतिरिक्त तरीके प्रदान करते हैं ।

वादे भविष्य के मूल्यों के लिए कंटेनर हैं। जब वादा मूल्य प्राप्त करता है (यह हल हो जाता है ) या जब इसे रद्द ( अस्वीकार ) किया जाता है, तो यह उसके सभी "श्रोताओं" को सूचित करता है जो इस मूल्य तक पहुंचना चाहते हैं।

सादे कॉलबैक से अधिक लाभ यह है कि वे आपको अपना कोड डिकूप करने की अनुमति देते हैं और उन्हें रचना करना आसान होता है।

यहाँ एक वादा का उपयोग करने का एक सरल उदाहरण है:

function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}

delay()
  .then(function(v) { // `delay` returns a promise
    console.log(v); // Log the value once it is resolved
  })
  .catch(function(v) {
    // Or do something else if it is rejected 
    // (it would not happen in this example, since `reject` is not called).
  });

हमारे अजाक्स कॉल के लिए लागू हम इस तरह से वादों का उपयोग कर सकते हैं:

function ajax(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open('GET', url);
    xhr.send();
  });
}

ajax("/echo/json")
  .then(function(result) {
    // Code depending on result
  })
  .catch(function() {
    // An error occurred
  });

उन सभी फायदों के बारे में बताना जो वादा करते हैं, इस उत्तर के दायरे से परे हैं, लेकिन यदि आप नया कोड लिखते हैं, तो आपको उन पर गंभीरता से विचार करना चाहिए। वे आपके कोड की एक महान अमूर्तता और अलगाव प्रदान करते हैं।

वादों के बारे में अधिक जानकारी: HTML5 चट्टानें - जावास्क्रिप्ट वादे

साइड नोट: jQuery के आस्थगित ऑब्जेक्ट

आस्थगित ऑब्जेक्ट्स jQuery के वादों का कस्टम कार्यान्वयन है (वादा एपीआई मानकीकृत होने से पहले)। वे लगभग वादों की तरह व्यवहार करते हैं लेकिन थोड़ा अलग एपीआई को उजागर करते हैं।

JQuery का हर अजाक्स तरीका पहले से ही एक "आस्थगित वस्तु" (वास्तव में एक आस्थगित वस्तु का एक वादा) लौटाता है जिसे आप अपने फ़ंक्शन से वापस कर सकते हैं:

function ajax() {
    return $.ajax(...);
}

ajax().done(function(result) {
    // Code depending on result
}).fail(function() {
    // An error occurred
});

साइड नोट: वादा किया गया है

ध्यान रखें कि वादे और आस्थगित वस्तुएं भविष्य के मूल्य के लिए सिर्फ कंटेनर हैं, वे स्वयं मूल्य नहीं हैं। उदाहरण के लिए, मान लें कि आपके पास निम्नलिखित थे:

function checkPassword() {
    return $.ajax({
        url: '/password',
        data: {
            username: $('#username').val(),
            password: $('#password').val()
        },
        type: 'POST',
        dataType: 'json'
    });
}

if (checkPassword()) {
    // Tell the user they're logged in
}

यह कोड उपरोक्त अतुल्यकालिक मुद्दों को गलत समझाता है। विशेष रूप से, $.ajax()कोड को फ्रीज नहीं करता है , जबकि यह आपके सर्वर पर '/ पासवर्ड' पृष्ठ की जांच करता है - यह सर्वर को एक अनुरोध भेजता है और प्रतीक्षा करते समय, यह तुरंत एक jQuery Ajax आस्थगित वस्तु देता है, न कि सर्वर से प्रतिक्रिया। इसका मतलब है कि ifबयान हमेशा इस आस्थगित वस्तु को प्राप्त करने के लिए जा रहा है, इसे इस तरह से व्यवहार करें true, और आगे बढ़ें जैसे कि उपयोगकर्ता लॉग इन है। अच्छा नहीं है।

लेकिन तय करना आसान है:

checkPassword()
.done(function(r) {
    if (r) {
        // Tell the user they're logged in
    } else {
        // Tell the user their password was bad
    }
})
.fail(function(x) {
    // Tell the user something bad happened
});

अनुशंसित नहीं: सिंक्रोनस "अजाक्स" कॉल

जैसा कि मैंने उल्लेख किया है, कुछ (!) अतुल्यकालिक संचालन में समकालिक समकक्ष हैं। मैं उनके उपयोग की वकालत नहीं करता, लेकिन पूर्णता के लिए, यहां बताया गया है कि आप एक तुल्यकालिक कॉल कैसे करेंगे:

बिना jQuery के

यदि आप सीधे किसी XMLHTTPRequestऑब्जेक्ट का उपयोग करते हैं , तो falseतीसरे तर्क के रूप में पास करें .open

jQuery

यदि आप jQuery का उपयोग करते हैं , तो आप asyncविकल्प को सेट कर सकते हैं false। ध्यान दें कि यह विकल्प jQuery 1.8 के बाद से हटा दिया गया है । आप तब या तो successकॉलबैक का उपयोग कर सकते हैं या jqXHR ऑब्जेक्ट की responseTextसंपत्ति तक पहुंच सकते हैं :

function foo() {
    var jqXHR = $.ajax({
        //...
        async: false
    });
    return jqXHR.responseText;
}

आप किसी भी अन्य jQuery अजाक्स विधि, के रूप में इस तरह के प्रयोग करते हैं $.get, $.getJSONआदि, आप के लिए इसे बदलने के लिए है $.ajax(चूंकि आप केवल करने के लिए कॉन्फ़िगरेशन पैरामीटर पारित कर सकते हैं $.ajax)।

सचेत! समकालिक JSONP अनुरोध करना संभव नहीं है । अपने बहुत ही स्वभाव से JSONP हमेशा अतुल्यकालिक है (इस विकल्प पर विचार नहीं करने का एक और कारण)।


74
@Pommy: यदि आप jQuery का उपयोग करना चाहते हैं, तो आपको इसे शामिल करना होगा। कृपया docs.jquery.com/Tutorials:Getting_Started_with_jQuery को देखें ।
फेलिक्स क्लिंग

11
समाधान 1 में, सब jQuery, मैं इस लाइन को समझ नहीं सका: If you use any other jQuery AJAX method, such as $.get, $.getJSON, etc., you have them to $.ajax.(हाँ, मुझे पता है कि मेरी निक इस मामले में एक विडंबना है)
cssyphus

31
@ गिब्बिश: मम्म, मुझे नहीं पता कि इसे कैसे स्पष्ट किया जा सकता है। क्या आप देखते हैं कि कैसे fooबुलाया जाता है और एक समारोह इसे पारित किया जाता है ( foo(function(result) {....});)? resultइस फ़ंक्शन के अंदर उपयोग किया जाता है और यह अजाक्स अनुरोध की प्रतिक्रिया है। इस फ़ंक्शन को संदर्भित करने के लिए, फू के पहले पैरामीटर को अनाम फ़ंक्शन के बजाय कॉल callbackऔर असाइन किया गया है success। इसलिए, अनुरोध सफल होने पर $.ajaxकॉल करेगा callback। मैंने इसे थोड़ा और समझाने की कोशिश की।
फेलिक्स क्लिंग

43
इस प्रश्न के लिए चैट मृत है, इसलिए मुझे यकीन नहीं है कि उल्लिखित परिवर्तनों का प्रस्ताव कहां है, लेकिन मैं प्रस्ताव करता हूं: 1) समकालिक भाग को एक साधारण चर्चा में बदलें कि यह कैसे करना है, इसका कोई कोड उदाहरण नहीं है। 2) कॉलबैक उदाहरणों को निकालें / मर्ज करें केवल और अधिक लचीले Deferred दृष्टिकोण को दिखाने के लिए, जो मुझे लगता है कि उन लोगों के लिए जावास्क्रिप्ट सीखना आसान हो सकता है।
क्रिस मोशचिनी

14
@ जेस्सी: मुझे लगता है कि आपने जवाब के उस हिस्से को गलत समझा। $.getJSONयदि आप चाहते हैं कि आप अजाक्स अनुरोध को तुल्यकालिक नहीं बना सकते हैं। हालाँकि, आपको नहीं चाहिए कि अनुरोध समकालिक हो, ताकि यह लागू न हो। आपको प्रतिक्रिया को संभालने के लिए कॉलबैक या वादों का उपयोग करना चाहिए, क्योंकि यह उत्तर में पहले ही समझाया गया है।
फेलिक्स क्लिंग

1071

यदि आप अपने कोड में jQuery का उपयोग नहीं कर रहे हैं , तो यह उत्तर आपके लिए है

आपका कोड इस की तर्ज पर कुछ होना चाहिए:

function foo() {
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
    return httpRequest.responseText;
}

var result = foo(); // always ends up being 'undefined'

एजेक्स के लिए jQuery का उपयोग करने वाले लोगों के लिए फेलिक्स क्लिंग ने एक अच्छा काम लिखा, मैंने उन लोगों के लिए एक विकल्प प्रदान करने का फैसला किया है जो नहीं हैं।

( नए fetchएपीआई, कोणीय या वादों का उपयोग करने वालों के लिए ध्यान दें, मैंने नीचे एक और उत्तर जोड़ा है )


आप क्या सामना कर रहे हैं

यह दूसरे उत्तर से "समस्या का स्पष्टीकरण" का एक संक्षिप्त सारांश है, यदि आप इसे पढ़ने के बाद सुनिश्चित नहीं हैं, तो पढ़ें।

एक AJAX में लिए खड़ा है अतुल्यकालिक । इसका मतलब है कि अनुरोध भेजना (या प्रतिक्रिया प्राप्त करना) सामान्य निष्पादन प्रवाह से बाहर ले जाया जाता है। आपके उदाहरण में, .sendतुरंत और अगले स्टेटमेंट को, return result;उस फ़ंक्शन से पहले निष्पादित किया जाता है जिसे आपने successकॉलबैक के रूप में पारित किया था।

इसका मतलब यह है कि जब आप वापस लौट रहे हैं, तो आपने जो श्रोता परिभाषित किया है, वह अभी तक निष्पादित नहीं हुआ है, जिसका अर्थ है कि आप जिस मूल्य पर लौट रहे हैं उसे परिभाषित नहीं किया गया है।

यहाँ एक सरल सादृश्य है

function getFive(){ 
    var a;
    setTimeout(function(){
         a=5;
    },10);
    return a;
}

(फिडल)

aलौटाया गया मान undefinedतब से है जब तक कि उस a=5हिस्से का निष्पादन नहीं हुआ है। AJAX इस तरह कार्य करता है, आप मान को वापस कर रहे हैं इससे पहले कि सर्वर को आपके ब्राउज़र को यह बताने का मौका मिले कि वह मूल्य क्या है।

इस समस्या का एक संभावित समाधान यह है कि जब गणना पूरी हो जाए तो अपने कार्यक्रम को फिर से सक्रिय रूप से कोड करें।

function onComplete(a){ // When the code completes, do this
    alert(a);
}

function getFive(whenDone){ 
    var a;
    setTimeout(function(){
         a=5;
         whenDone(a);
    },10);
}

इसे CPS कहा जाता है । असल में, getFiveजब हम इसे पूरा करते हैं, तो प्रदर्शन करने के लिए एक कार्रवाई कर रहे हैं, हम अपना कोड बता रहे हैं कि जब कोई घटना (जैसे हमारे AJAX कॉल, या इस मामले में समय समाप्त) पर प्रतिक्रिया कैसे करें।

उपयोग होगा:

getFive(onComplete);

जिसे स्क्रीन पर "5" को अलर्ट करना चाहिए। (फिडेल)

संभव समाधान

मूल रूप से इसे हल करने के दो तरीके हैं:

  1. AJAX कॉल को सिंक्रोनस करें (इसे SJAX कहते हैं)।
  2. कॉलबैक के साथ ठीक से काम करने के लिए अपने कोड को पुनर्स्थापित करें।

1. तुल्यकालिक AJAX - यह मत करो !!

तुल्यकालिक AJAX के लिए, यह मत करो! फेलिक्स का जवाब इस बारे में कुछ मजबूर तर्क देता है कि यह एक बुरा विचार क्यों है। इसे योग करने के लिए, यह उपयोगकर्ता के ब्राउज़र को तब तक फ्रीज करेगा, जब तक कि सर्वर प्रतिक्रिया को वापस नहीं करता है और एक बहुत बुरा उपयोगकर्ता अनुभव बनाता है। यहाँ MDN से लिया गया एक और संक्षिप्त सारांश क्यों दिया गया है:

XMLHttpRequest सिंक्रोनस और एसिंक्रोनस संचार का समर्थन करता है। सामान्य तौर पर, हालांकि, अतुल्यकालिक अनुरोधों को प्रदर्शन कारणों के लिए सिंक्रोनस अनुरोधों के लिए प्राथमिकता दी जानी चाहिए।

संक्षेप में, तुल्यकालिक अनुरोध कोड के निष्पादन को अवरुद्ध करते हैं ... ... इससे गंभीर समस्याएं हो सकती हैं ...

आप तो है यह करने के लिए, आप एक ध्वज पारित कर सकते हैं: यहाँ है कैसे:

var request = new XMLHttpRequest();
request.open('GET', 'yourURL', false);  // `false` makes the request synchronous
request.send(null);

if (request.status === 200) {// That's HTTP for 'ok'
  console.log(request.responseText);
}

2. पुनर्गठन कोड

अपने फ़ंक्शन को कॉलबैक स्वीकार करने दें। उदाहरण के fooलिए एक कॉलबैक स्वीकार करने के लिए कोड बनाया जा सकता है। हम अपना कोड बता रहे हैं कि कैसे पूरा होने पर प्रतिक्रिया देंfoo

इसलिए:

var result = foo();
// code that depends on `result` goes here

हो जाता है:

foo(function(result) {
    // code that depends on `result`
});

यहां हमने एक अनाम फ़ंक्शन पास किया, लेकिन हम किसी मौजूदा फ़ंक्शन के संदर्भ को आसानी से पास कर सकते हैं, जिससे यह ऐसा दिखता है:

function myHandler(result) {
    // code that depends on `result`
}
foo(myHandler);

इस प्रकार के कॉलबैक डिज़ाइन के बारे में अधिक जानकारी के लिए, फेलिक्स के उत्तर की जाँच करें।

अब, तदनुसार कार्य करने के लिए स्वयं को परिभाषित करते हैं

function foo(callback) {
    var httpRequest = new XMLHttpRequest();
    httpRequest.onload = function(){ // when the request is loaded
       callback(httpRequest.responseText);// we're calling our method
    };
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
}

(बेला)

हमने अब अपने फू फंक्शन को चलाने के लिए एक एक्शन स्वीकार कर लिया है जब AJAX सफलतापूर्वक पूरा होता है, हम इसे आगे की जाँच कर सकते हैं यदि प्रतिक्रिया की स्थिति 200 नहीं है और तदनुसार कार्य कर रहा है (एक असफल हैंडलर और ऐसे बनाएं)। हमारे मुद्दे को प्रभावी ढंग से हल करना।

यदि आपको अभी भी यह समझने में कठिन समय हो रहा है कि AJAX को MDN में गाइड शुरू करना पढ़ें


20
"सिंक्रोनस अनुरोध कोड के निष्पादन को रोकते हैं और मेमोरी और घटनाओं को लीक कर सकते हैं" एक सिंक्रोनस अनुरोध लीक मेमोरी कैसे कर सकता है?
मैथ्यू जी

10
@MatthewG मैंने इस प्रश्न पर एक इनाम जोड़ा है , मैं देखूंगा कि मैं क्या कर सकता हूं। मैं मतलब समय में जवाब से उद्धरण हटा रहा हूं।
बेंजामिन ग्रुएनबाउम

17
बस संदर्भ के लिए, XHR 2 हमें onloadहैंडलर का उपयोग करने की अनुमति देता है , जो केवल जब readyStateहोता है तो आग लग जाती है 4। बेशक, यह IE8 में समर्थित नहीं है। (आईसीसी, पुष्टि की आवश्यकता हो सकती है।)
फ्लोरियन मार्गाइन

9
कॉलबैक के रूप में एक अनाम फ़ंक्शन को कैसे पास किया जाए, इस बारे में आपकी व्याख्या वैध लेकिन भ्रामक है। उदाहरण var बार = foo (); एक वैरिएबल को परिभाषित करने के लिए कह रहा है, जबकि आपके सुझाए गए फू (फंक्शनलिम () {}); बार को परिभाषित नहीं करता
रोबी एवरिल

396

XMLHttpRequest 2 (सबसे पहले बेंजामिन ग्रुएनबाम और फेलिक्स क्लिंग के जवाब पढ़ें)

यदि आप jQuery का उपयोग नहीं करते हैं और एक अच्छा लघु XMLHttpRequest 2 चाहते हैं जो आधुनिक ब्राउज़रों पर काम करता है और मोबाइल ब्राउज़रों पर भी मैं इसे इस तरह उपयोग करने का सुझाव देता हूं:

function ajax(a, b, c){ // URL, callback, just a placeholder
  c = new XMLHttpRequest;
  c.open('GET', a);
  c.onload = b;
  c.send()
}

जैसा कि आप देख सकते हैं:

  1. यह सूचीबद्ध अन्य सभी कार्यों से छोटा है।
  2. कॉलबैक को सीधे सेट किया जाता है (ताकि कोई अतिरिक्त अनावश्यक बंद न हो)।
  3. यह नए ऑनलोड का उपयोग करता है (ताकि आपको रीडिस्टेट और& स्थिति के लिए जांच न करनी पड़े)
  4. कुछ अन्य स्थितियां हैं जो मुझे याद नहीं हैं कि XMLHttpRequest 1 को कष्टप्रद बनाते हैं।

इस अजाक्स कॉल की प्रतिक्रिया प्राप्त करने के दो तरीके हैं (तीन XMLHttpRequest var name का उपयोग करके):

सबसे आसान:

this.response

या यदि किसी कारण से आप bind()किसी कक्षा में कॉलबैक करते हैं:

e.target.response

उदाहरण:

function callback(e){
  console.log(this.response);
}
ajax('URL', callback);

या (ऊपर वाला बेहतर अनाम कार्य हमेशा एक समस्या है):

ajax('URL', function(e){console.log(this.response)});

कुछ भी आसान नहीं है।

अब कुछ लोग शायद कहेंगे कि onreadystatechange या यहाँ तक कि XMLHttpRequest वैरिएबल नाम का उपयोग करना बेहतर है। यह गलत है।

XMLHttpRequest उन्नत सुविधाओं की जाँच करें

इसने सभी * आधुनिक ब्राउज़रों का समर्थन किया। और मैं पुष्टि कर सकता हूं क्योंकि मैं XMLHttpRequest 2 मौजूद होने के बाद से इस दृष्टिकोण का उपयोग कर रहा हूं। मेरे द्वारा उपयोग किए जाने वाले सभी ब्राउज़रों पर मुझे कभी किसी प्रकार की समस्या नहीं हुई।

यदि आप राज्य 2 पर हेडर प्राप्त करना चाहते हैं तो onreadystatechange केवल उपयोगी है।

XMLHttpRequestवैरिएबल नाम का उपयोग करना एक और बड़ी त्रुटि है क्योंकि आपको ऑनलोड / oreadystatechange क्लोजर के अंदर कॉलबैक को निष्पादित करने की आवश्यकता है जो आपने इसे खो दिया है।


अब यदि आप पोस्ट और फॉर्मडाटा का उपयोग करके कुछ और जटिल चाहते हैं, तो आप इस फ़ंक्शन को आसानी से बढ़ा सकते हैं:

function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val},placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.send(d||null)
}

फिर से ... यह एक बहुत ही छोटा कार्य है, लेकिन यह मिलता है और पोस्ट करता है।

उपयोग के उदाहरण:

x(url, callback); // By default it's get so no need to set
x(url, callback, 'post', {'key': 'val'}); // No need to set post data

या फुल फॉर्म एलिमेंट पास करें ( document.getElementsByTagName('form')[0]):

var fd = new FormData(form);
x(url, callback, 'post', fd);

या कुछ कस्टम मान सेट करें:

var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);

जैसा कि आप देख सकते हैं कि मैंने सिंक को लागू नहीं किया है ... यह एक बुरी बात है।

कहा जा रहा है कि ... यह आसान तरीका क्यों नहीं है?


जैसा कि टिप्पणी में उल्लेख किया गया है कि त्रुटि और& सिंक्रोनस का उपयोग उत्तर के बिंदु को पूरी तरह से तोड़ देता है। अजाक्स को उचित तरीके से उपयोग करने के लिए कौन सा अच्छा तरीका है?

त्रुटि करने वाला

function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val}, placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.onerror = error;
  c.send(d||null)
}

function error(e){
  console.log('--Error--', this.type);
  console.log('this: ', this);
  console.log('Event: ', e)
}
function displayAjax(e){
  console.log(e, this);
}
x('WRONGURL', displayAjax);

उपरोक्त स्क्रिप्ट में, आपके पास एक त्रुटि हैंडलर है जो कि सांख्यिकीय रूप से परिभाषित है, इसलिए यह फ़ंक्शन से समझौता नहीं करता है। त्रुटि हैंडलर का उपयोग अन्य कार्यों के लिए भी किया जा सकता है।

लेकिन वास्तव में एक त्रुटि को बाहर निकालने का एकमात्र तरीका एक गलत URL लिखना है जिस स्थिति में प्रत्येक ब्राउज़र एक त्रुटि फेंकता है।

त्रुटि हैंडलर शायद उपयोगी होते हैं यदि आप कस्टम हेडर सेट करते हैं, तो रिस्पॉन्स टाइप को ब्लॉ एरे बफर या जो भी सेट करें ...

यहां तक ​​कि अगर आप 'POSTAPAPAP' विधि से गुजरते हैं, तो यह एक त्रुटि नहीं है।

यहां तक ​​कि अगर आप 'fdggdgilfdghfldj' को formdata के रूप में पास करते हैं तो यह एक त्रुटि नहीं होगी।

पहले मामले में त्रुटि displayAjax()अंडर this.statusTextमें है Method not Allowed

दूसरे मामले में, यह बस काम करता है। यदि आपने सही पोस्ट डेटा पास किया है तो आपको सर्वर की तरफ देखना होगा।

क्रॉस-डोमेन ने स्वचालित रूप से थ्रो त्रुटि की अनुमति नहीं दी है।

त्रुटि प्रतिक्रिया में, कोई त्रुटि कोड नहीं हैं।

केवल वही है this.typeजो त्रुटि पर सेट है।

यदि आप पूरी तरह से त्रुटियों पर नियंत्रण नहीं रखते हैं, तो एक त्रुटि हैंडलर क्यों जोड़ें? कॉलबैक फ़ंक्शन में इसके अंदर अधिकांश त्रुटियां दी गई हैं displayAjax()

इसलिए: यदि आप URL को ठीक से कॉपी और पेस्ट करने में सक्षम हैं तो त्रुटि जांच की कोई आवश्यकता नहीं है। ;)

पुनश्च: पहले परीक्षण के रूप में मैंने x ('x', displayAjax) लिखा था ... और इसे पूरी तरह से प्रतिक्रिया मिली ... ??? इसलिए मैंने उस फ़ोल्डर की जाँच की जहाँ HTML स्थित है, और 'x.xml' नामक एक फ़ाइल थी। तो भले ही आप अपनी फ़ाइल का एक्सटेंशन XMLHttpRequest 2 WILL FIND IT भूल जाएं । I LOL'd


एक फ़ाइल सिंक्रोनस पढ़ें

ऐसा मत करो।

यदि आप ब्राउज़र को थोड़ी देर के लिए ब्लॉक करना चाहते हैं तो एक अच्छी बड़ी .txtफाइल को सिंक्रोनाइज़ करें।

function omg(a, c){ // URL
  c = new XMLHttpRequest;
  c.open('GET', a, true);
  c.send();
  return c; // Or c.response
}

अब आप कर सकते हैं

 var res = omg('thisIsGonnaBlockThePage.txt');

गैर-अतुल्यकालिक तरीके से ऐसा करने का कोई और तरीका नहीं है। (हाँ, सेटटाइमआउट लूप के साथ ... लेकिन गंभीरता से?)

एक और बिंदु है ... यदि आप एपीआई या अपनी खुद की सूची की फाइलों या जो भी आप हमेशा प्रत्येक अनुरोध के लिए अलग-अलग कार्यों का उपयोग करते हैं, के साथ काम करते हैं ...

यदि आपके पास एक पृष्ठ है जहाँ आप हमेशा एक ही XML / JSON लोड करते हैं या आपको केवल एक फ़ंक्शन की आवश्यकता होती है। उस स्थिति में, अजाक्स फ़ंक्शन को थोड़ा संशोधित करें और अपने विशेष फ़ंक्शन के साथ बी को बदलें।


उपरोक्त कार्य बुनियादी उपयोग के लिए हैं।

यदि आप फ़ंक्शन का विस्तार करना चाहते हैं ...

हाँ तुम कर सकते हो।

मैं बहुत सारे API का उपयोग कर रहा हूं और प्रत्येक HTML पेज में जो पहला फ़ंक्शन मैं एकीकृत करता हूं, वह इस उत्तर में पहला अजाक्स फ़ंक्शन है, केवल टीईटी के साथ ...

लेकिन आप XMLHttpRequest 2 के साथ बहुत कुछ कर सकते हैं:

मैंने एक डाउनलोड प्रबंधक बनाया (फिर से शुरू, फ़िलेडर, फाइलसिस्टम के साथ दोनों पक्षों पर श्रेणियों का उपयोग करते हुए), कैनवास का उपयोग करते हुए विभिन्न छवि पुनर्विक्रेताओं कन्वर्टर्स, बेस 64images के साथ वेब SQL डेटाबेस को पॉप्युलेट करते हैं और बहुत कुछ ... लेकिन इन मामलों में आपको केवल एक फ़ंक्शन बनाना चाहिए उद्देश्य ... कभी-कभी आपको एक बूँद, सरणी बफ़र्स की आवश्यकता होती है, आप हेडर सेट कर सकते हैं, mimetype को ओवरराइड कर सकते हैं और बहुत कुछ है ...

लेकिन यहाँ सवाल यह है कि अजाक्स प्रतिक्रिया कैसे लौटाया जाए ... (मैंने एक आसान तरीका जोड़ा।)


15
हालांकि यह उत्तर अच्छा है (और हम सभी को एक्सएचआर 2 से प्यार है और पोस्ट डेटा और मल्टीपार्ट डेटा पोस्ट करना पूरी तरह से भयानक है) - यह जावास्क्रिप्ट के साथ एक्सएचआर पोस्ट करने के लिए सिंटैक्टिक चीनी दिखाता है - आप इसे ब्लॉग पोस्ट में डालना चाहते हैं (मुझे यह पसंद आएगा) या यहां तक ​​कि एक पुस्तकालय में (नाम के बारे में निश्चित नहीं है x, ajaxया xhrअच्छा हो सकता है :))। मैं यह नहीं देखता कि यह AJAX कॉल से प्रतिक्रिया को कैसे संबोधित करता है। (कोई अभी भी कर सकता है var res = x("url")और समझ नहीं सकता कि यह काम क्यों नहीं करता है;))। एक तरफ ध्यान दें - यह अच्छा होगा यदि आप cविधि से लौटे हैं तो उपयोगकर्ता errorआदि को हुक कर सकते हैं
बेंजामिन ग्रुएनबाम

25
2.ajax is meant to be async.. so NO var res=x('url')..यह इस सवाल और जवाब का पूरा बिंदु है :)
बेंजामिन Gruenbaum

3
फ़ंक्शंस में 'c' पैरामीटर क्यों है, यदि पहली पंक्ति में आप जो कुछ भी मान रहे हैं, उसे ओवरराइट कर रहे हैं? क्या मैं कुछ भूल रहा हूँ?
ब्रायन एच।

2
आप कई बार "var" लिखने से बचने के लिए एक प्लेसहोल्डर के रूप में मापदंडों का उपयोग कर सकते हैं
cocco

11
@ कोको तो आपने कुछ कीस्ट्रोक्स को बचाने के लिए एक SO उत्तर में भ्रामक, अपठनीय कोड लिखा ? कृपया ऐसा मत करो।
पत्थर

316

यदि आप वादों का उपयोग कर रहे हैं, तो यह उत्तर आपके लिए है।

इसका मतलब है AngularJS, jQuery (आस्थगित के साथ), देशी XHR के प्रतिस्थापन (लाने), EmberJS, BackboneJS की बचत या कोई भी नोड लाइब्रेरी जो वादे लौटाती है।

आपका कोड इस की तर्ज पर कुछ होना चाहिए:

function foo() {
    var data;
    // or $.get(...).then, or request(...).then, or query(...).then
    fetch("/echo/json").then(function(response){
        data = response.json();
    });
    return data;
}

var result = foo(); // result is always undefined no matter what.

फेलिक्स क्लिंग ने AJAX के लिए कॉलबैक के साथ jQuery का उपयोग करने वाले लोगों के लिए एक जवाब लिखने का एक अच्छा काम किया। मेरे पास देशी XHR के लिए एक उत्तर है। यह जवाब या तो फ्रंटएंड या बैकएंड पर वादों के सामान्य उपयोग के लिए है।


मूल मुद्दा है

ब्राउज़र में जावास्क्रिप्ट कॉन्सेप्ट मॉडल और NodeJS / io.js के साथ सर्वर पर अतुल्यकालिक और प्रतिक्रियाशील है

जब भी आप किसी वादे को वापस करने वाली विधि कहते हैं, तो thenहैंडलर को हमेशा एसिंक्रोनस रूप से निष्पादित किया जाता है - अर्थात, उनके नीचे कोड के बाद जो .thenहैंडलर में नहीं होता है ।

इसका मतलब है आप के लिए लौट रहे जब हैंडलर द्वारा निर्धारित किए गए अभी तक अमल नहीं किया। बदले में इसका मतलब है कि आप जो मूल्य वापस कर रहे हैं वह समय में सही मूल्य पर सेट नहीं किया गया है।datathen

यहाँ इस मुद्दे के लिए एक सरल सादृश्य है:

    function getFive(){
        var data;
        setTimeout(function(){ // set a timer for one second in the future
           data = 5; // after a second, do this
        }, 1000);
        return data;
    }
    document.body.innerHTML = getFive(); // `undefined` here and not 5

मान का dataवह हिस्सा है जिसके undefinedबाद से data = 5भाग अभी तक निष्पादित नहीं किया गया है। यह संभवतः एक सेकंड में निष्पादित होगा लेकिन उस समय तक यह लौटाए गए मूल्य के लिए अप्रासंगिक है।

चूंकि ऑपरेशन अभी तक नहीं हुआ (AJAX, सर्वर कॉल, IO, टाइमर) आप मूल्य वापस कर रहे हैं इससे पहले कि अनुरोध को आपके कोड को बताने का मौका मिला कि वह मूल्य क्या है।

इस समस्या का एक संभावित समाधान यह है कि जब गणना पूरी हो जाए तो अपने कार्यक्रम को फिर से सक्रिय रूप से कोड करें। वादे सक्रिय रूप से प्रकृति में लौकिक (समय के प्रति संवेदनशील) होने के द्वारा इसे सक्षम करते हैं।

वादों पर त्वरित टालमटोल

समय के साथ एक वादा एक मूल्य है । वादों की स्थिति होती है, वे बिना किसी मूल्य के लंबित होने लगते हैं और इसके लिए व्यवस्थित हो सकते हैं:

  • पूरा जिसका अर्थ है कि गणना सफलतापूर्वक पूर्ण हुआ।
  • अस्वीकार कर दिया कि अभिकलन विफल हो गया।

एक वादा केवल एक बार राज्यों को बदल सकता है जिसके बाद वह हमेशा एक ही राज्य में रहेगा। आप thenअपने मूल्य निकालने और त्रुटियों को संभालने के वादे के लिए हैंडलर संलग्न कर सकते हैं। thenहैंडलर कॉल को चैन करने की अनुमति देते हैं। वादे एपीआई का उपयोग करके बनाए जाते हैं जो उन्हें वापस करते हैं । उदाहरण के लिए, अधिक आधुनिक AJAX प्रतिस्थापन fetchया jQuery के $.getरिटर्न वादे।

जब हम .thenकिसी वादे पर कॉल करते हैं और उसमें से कुछ लौटाते हैं - हमें संसाधित मूल्य के लिए एक वादा मिलता है । यदि हम एक और वादा वापस करते हैं तो हमें आश्चर्यजनक चीजें मिलेंगी, लेकिन चलो हमारे घोड़े पकड़ें।

वादों के साथ

आइए देखें कि हम उपरोक्त मुद्दों को वादों के साथ कैसे हल कर सकते हैं। सबसे पहले, एक देरी फ़ंक्शन बनाने के लिए वादा निर्माता का उपयोग करके ऊपर से वादा राज्यों की हमारी समझ प्रदर्शित करें:

function delay(ms){ // takes amount of milliseconds
    // returns a new promise
    return new Promise(function(resolve, reject){
        setTimeout(function(){ // when the time is up
            resolve(); // change the promise to the fulfilled state
        }, ms);
    });
}

अब, हम वादों का उपयोग करने के लिए सेटटाइमआउट में परिवर्तित होने के बाद, हम thenइसे गिनने के लिए उपयोग कर सकते हैं :

function delay(ms){ // takes amount of milliseconds
  // returns a new promise
  return new Promise(function(resolve, reject){
    setTimeout(function(){ // when the time is up
      resolve(); // change the promise to the fulfilled state
    }, ms);
  });
}

function getFive(){
  // we're RETURNING the promise, remember, a promise is a wrapper over our value
  return delay(100).then(function(){ // when the promise is ready
      return 5; // return the value 5, promises are all about return values
  })
}
// we _have_ to wrap it like this in the call site, we can't access the plain value
getFive().then(function(five){ 
   document.body.innerHTML = five;
});

मूल रूप से, एक लौटने के बजाय मूल्य है जो हम क्योंकि संगामिति मॉडल के लिए नहीं कर सकते हैं - हम एक लौट रहे हैं आवरण एक मूल्य है कि हम कर सकते हैं के लिए खोलने के साथ then। यह एक बॉक्स की तरह है जिसे आप खोल सकते हैं then

इसे लागू करना

यह आपकी मूल API कॉल के लिए समान है, आप यह कर सकते हैं:

function foo() {
    // RETURN the promise
    return fetch("/echo/json").then(function(response){
        return response.json(); // process it inside the `then`
    });
}

foo().then(function(response){
    // access the value inside the `then`
})

तो यह भी काम करता है। हमने सीखा है कि हम पहले से ही अतुल्यकालिक कॉल से मूल्यों को वापस नहीं कर सकते हैं लेकिन हम वादे का उपयोग कर सकते हैं और उन्हें प्रसंस्करण के लिए श्रृंखला बना सकते हैं। अब हम जानते हैं कि एक एसिंक्रोनस कॉल से प्रतिक्रिया कैसे वापस करें।

ES2015 (ES6)

ईएस 6 जनरेटर उत्पन्न करता है जो ऐसे कार्य हैं जो बीच में वापस आ सकते हैं और फिर उस बिंदु को फिर से शुरू कर सकते हैं जो वे थे। उदाहरणों के लिए, यह आम तौर पर उपयोगी होता है:

function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
    yield 1;
    yield 2;
    while(true) yield 3;
}

एक समारोह है कि एक रिटर्न है इटरेटर अनुक्रम से अधिक 1,2,3,3,3,3,....जो दोहराया जा सकता है। हालांकि यह अपने आप में दिलचस्प है और बहुत अधिक संभावना के लिए कमरा खोलता है एक विशेष दिलचस्प मामला है।

यदि हम जो अनुक्रम बना रहे हैं, वह संख्याओं के बजाय क्रियाओं का अनुक्रम है - हम फ़ंक्शन को रोक सकते हैं जब भी कोई कार्रवाई की जाती है और फ़ंक्शन को फिर से शुरू करने से पहले इसके लिए प्रतीक्षा करें। इसलिए संख्याओं के अनुक्रम के बजाय, हमें भविष्य के मूल्यों के अनुक्रम की आवश्यकता है - अर्थात: वादे।

यह कुछ हद तक मुश्किल लेकिन बहुत शक्तिशाली चाल है जो हमें सिंक्रोनस कोड को समकालिक तरीके से लिखने की सुविधा देती है। कई "धावक" हैं जो आपके लिए ऐसा करते हैं, एक लिखना कोड की कुछ ही पंक्तियाँ हैं लेकिन इस उत्तर के दायरे से परे है। मैं Promise.coroutineयहाँ Bluebird का उपयोग कर रहा हूँ , लेकिन coया जैसे अन्य आवरण हैं Q.async

var foo = coroutine(function*(){
    var data = yield fetch("/echo/json"); // notice the yield
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
});

यह विधि एक वादा खुद लौटाती है, जिसका हम अन्य कोरटाइन से उपभोग कर सकते हैं। उदाहरण के लिए:

var main = coroutine(function*(){
   var bar = yield foo(); // wait our earlier coroutine, it returns a promise
   // server call done here, code below executes when done
   var baz = yield fetch("/api/users/"+bar.userid); // depends on foo's result
   console.log(baz); // runs after both requests done
});
main();

ES2016 (ES7)

ES7 में, इसे और मानकीकृत किया गया है, अभी कई प्रस्ताव हैं लेकिन उन सभी में आप awaitवादा कर सकते हैं । यह ऊपर asyncऔर awaitकीवर्ड जोड़कर ES6 प्रस्ताव के लिए सिर्फ "चीनी" (अच्छे सिंटैक्स) है । उपरोक्त उदाहरण बनाना:

async function foo(){
    var data = await fetch("/echo/json"); // notice the await
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
}

यह अभी भी एक ही वादा करता है :)


यह स्वीकृत उत्तर होना चाहिए। +1 async / प्रतीक्षा के लिए (हालाँकि हमें नहीं करना चाहिए return await data.json();?)
लुईस डोनोवन

247

आप अजाक्स का गलत उपयोग कर रहे हैं। विचार यह नहीं है कि इसे कुछ भी लौटाया जाए, बल्कि डेटा को कॉलबैक फ़ंक्शन नाम की चीज़ को सौंप दिया जाए, जो डेटा को संभालता है।

अर्थात्:

function handleData( responseData ) {

    // Do what you want with the data
    console.log(responseData);
}

$.ajax({
    url: "hi.php",
    ...
    success: function ( data, status, XHR ) {
        handleData(data);
    }
});

सबमिट हैंडलर में कुछ भी लौटाने से कुछ नहीं होगा। आपको इसके बजाय या तो डेटा को बंद करना चाहिए, या ऐसा करना चाहिए जो आप सीधे सफलता समारोह के अंदर चाहते हैं।


13
यह उत्तर पूरी तरह से शब्दार्थ है ... आपकी सफलता की विधि एक कॉलबैक के भीतर एक कॉलबैक है। तुम बस हो सकता है success: handleDataऔर यह काम करेगा।
जैक्स Jac ques ques ques

5
और क्या होगा अगर आप "handleData" के बाहर "responseData" वापस करना चाहते हैं ... :) ... आप इसे कैसे करेंगे ...? ... कारण एक साधारण वापसी अजाक्स की "सफलता" कॉलबैक पर लौटेगी ... और "हैंडलडैटा" के बाहर नहीं ...
पेशो hristov

@Jacques & @pesho hristov आप इस बिंदु से चूक गए। सबमिट हैंडलर successविधि नहीं है , यह आसपास का दायरा है $.ajax
ट्रैवनिक

@travnik मुझे वह याद नहीं था। यदि आप हैंडलडैट की सामग्री लेते हैं और इसे सफलता की विधि में डालते हैं तो यह बिल्कुल उसी तरह काम करेगा ...
जैक्स ッ

234

सबसे सरल समाधान एक जावास्क्रिप्ट फ़ंक्शन बनाता है और इसे अजाक्स successकॉलबैक के लिए कहता है ।

function callServerAsync(){
    $.ajax({
        url: '...',
        success: function(response) {

            successCallback(response);
        }
    });
}

function successCallback(responseObj){
    // Do something like read the response and show data
    alert(JSON.stringify(responseObj)); // Only applicable to JSON response
}

function foo(callback) {

    $.ajax({
        url: '...',
        success: function(response) {
           return callback(null, response);
        }
    });
}

var result = foo(function(err, result){
          if (!err)
           console.log(result);    
}); 

3
मुझे नहीं पता कि किसने इसे नकारात्मक वोट दिया। लेकिन यह एक ऐसा काम है जिसके चारों ओर वास्तव में मैंने काम किया है मैंने इस दृष्टिकोण का उपयोग एक संपूर्ण एप्लिकेशन बनाने के लिए किया है। Jquery.ajax उपरोक्त दृष्टिकोण का उपयोग करने के लिए डेटा को बेहतर नहीं लौटाता है। अगर यह गलत है तो कृपया समझाएं और इसे करने का बेहतर तरीका सुझाएं।
हेमंत बावले

11
क्षमा करें, मैं एक टिप्पणी छोड़ना भूल गया (मैं आमतौर पर करता हूं!)। मैंने इसे नीचा दिखाया। डाउनवोट्स तथ्यात्मक शुद्धता या कमी का संकेत नहीं देते हैं, वे संदर्भ या उपयोगिता में कमी का संकेत देते हैं। मुझे आपका उत्तर उपयोगी नहीं दिया गया है जो फेलिक्स ने दिया है जो पहले से ही इसे और अधिक विस्तार से बताता है। एक साइड नोट पर, यदि JSON है तो आप प्रतिक्रिया को कड़े क्यों करेंगे?
बेंजामिन ग्रुएनबाम

5
ठीक है .. @ बेंजामिन मैंने स्ट्रिंग का उपयोग किया, एक JSON ऑब्जेक्ट को स्ट्रिंग में बदलने के लिए। और अपनी बात स्पष्ट करने के लिए धन्यवाद। अधिक विस्तृत उत्तर पोस्ट करने के लिए ध्यान में रखेंगे।
हेमंत बावले

और क्या होगा अगर आप "successCallback" के बाहर "responseObj" को वापस करना चाहते हैं ... :) ... आप इसे कैसे करेंगे ...? ... कारण एक साधारण वापसी अजाक्स की "सफलता" कॉलबैक पर लौटेगी ... और "सफलता के पीछे" नहीं "कॉलबैक" ...
पेशो hristov

221

मैं एक भयानक-दिखने वाले, हाथ से तैयार कॉमिक के साथ जवाब दूंगा। दूसरी छवि यही कारण है कि आपके कोड उदाहरण में क्यों resultहै undefined

यहां छवि विवरण दर्ज करें


32
एक तस्वीर एक हजार शब्दों के लायक है , व्यक्ति ए - व्यक्ति के बी से उसकी कार को ठीक करने के लिए पूछें, बदले में व्यक्ति बी - अजाक्स कॉल करता है और कार फिक्सिंग विवरण के लिए सर्वर से प्रतिक्रिया की प्रतीक्षा करता है, जब प्रतिक्रिया प्राप्त होती है, तो अजाक्स सक्सेस फंक्शन व्यक्ति को बुलाता है। बी कार्य करता है और प्रतिक्रिया को तर्क के रूप में पारित करता है, व्यक्ति ए को जवाब मिलता है।
शाइजुत

10
यदि आप अवधारणाओं को चित्रित करने के लिए प्रत्येक छवि के साथ कोड की लाइनें जोड़ते हैं तो बहुत अच्छा होगा।
हसन बेग

1
इस बीच, कार वाला लड़का सड़क के किनारे फंस गया। उसे जारी रखने से पहले कार की आवश्यकता होती है। वह अब सड़क के किनारे अकेले इंतज़ार कर रहा है ... वह स्थिति बदलने की प्रतीक्षा में फोन पर रहेगा, लेकिन मैकेनिक ऐसा नहीं करेगा ... मैकेनिक ने कहा कि उसे अपनी नौकरी करनी है और नहीं कर सकते बस फोन पर बाहर लटका। मैकेनिक ने वादा किया कि वह जल्द से जल्द उसे वापस बुलाएगा। लगभग 4 घंटे के बाद, लड़का उबेर को फोन करता है। - टाइमआउट का उदाहरण।
बैरीपिकर

@barrypicker :- डी ब्रिलियंट!
जोहान्स फारेनक्रग

159

Angular1

जो लोग AngularJS का उपयोग कर रहे हैं , वे इस स्थिति का उपयोग करके संभाल सकते हैं Promises

यहाँ यह कहता है,

वादों को अतुल्यकालिक कार्यों के लिए इस्तेमाल किया जा सकता है और एक साथ कई कार्यों को चेन करने की अनुमति देता है।

आप यहाँ एक अच्छी व्याख्या भी पा सकते हैं ।

उदाहरण नीचे दिए गए डॉक्स में मिला ।

  promiseB = promiseA.then(
    function onSuccess(result) {
      return result + 1;
    }
    ,function onError(err) {
      //Handle error
    }
  );

 // promiseB will be resolved immediately after promiseA is resolved 
 // and its value will be the result of promiseA incremented by 1.

कोणीय 2 और बाद में

में Angular2निम्नलिखित उदाहरण को देखो, लेकिन इसके साथ की सिफारिश की उपयोग करने के लिए Observablesके साथ Angular2

 search(term: string) {
     return this.http
  .get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)
  .map((response) => response.json())
  .toPromise();

}

आप इस तरह से उपभोग कर सकते हैं,

search() {
    this.searchService.search(this.searchField.value)
      .then((result) => {
    this.result = result.artists.items;
  })
  .catch((error) => console.error(error));
}

मूल पोस्ट यहाँ देखें । लेकिन टाइपस्क्रिप्ट देशी es6 वादों का समर्थन नहीं करता है , यदि आप इसका उपयोग करना चाहते हैं, तो आपको इसके लिए प्लगइन की आवश्यकता हो सकती है।

इसके अतिरिक्त यहाँ वादे युक्ति यहाँ परिभाषित है।


15
यह स्पष्ट नहीं करता है कि वादे इस मुद्दे को कैसे हल करेंगे।
बेंजामिन ग्रुएनबाम

4
jQuery और लाने के तरीके दोनों ही वादे वापस करते हैं। मैं आपके उत्तर को संशोधित करने का सुझाव दूंगा। हालांकि jQuery के काफी समान नहीं है (फिर वहाँ है, लेकिन पकड़ नहीं है)।
ट्रैकर

153

जब आप एक एकल async ऑपरेशन करते हैं, तो अधिकांश उत्तर यहां उपयोगी सुझाव देते हैं, लेकिन कभी-कभी, यह तब आता है जब आपको किसी सरणी या अन्य सूची-जैसी संरचना में प्रत्येक प्रविष्टि के लिए एक अतुल्यकालिक ऑपरेशन करने की आवश्यकता होती है। प्रलोभन यह करने के लिए है:

// WRONG
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log(results); // E.g., using them, returning them, etc.

उदाहरण:

कारण यह है कि काम नहीं करता है कि जब doSomethingAsyncतक आप परिणामों का उपयोग करने की कोशिश कर रहे हैं, तब तक कॉलबैक अभी तक नहीं चला है।

इसलिए, यदि आपके पास एक सरणी है (या किसी प्रकार की सूची) और प्रत्येक प्रविष्टि के लिए async संचालन करना चाहते हैं, तो आपके पास दो विकल्प हैं: ऑपरेशन को समानांतर (ओवरलैपिंग) में करें, या श्रृंखला में (क्रम में एक के बाद एक) करें।

समानांतर

आप उन सभी को शुरू कर सकते हैं और आप कितने कॉलबैक की अपेक्षा कर रहे हैं, इसका ट्रैक रख सकते हैं, और जब आपने कई कॉलबैक प्राप्त कर लिए हैं तो परिणामों का उपयोग करें:

var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

उदाहरण:

(हम दूर कर सकते हैं expectingऔर सिर्फ उपयोग कर सकते हैं results.length === theArray.length, लेकिन यह हमें उस संभावना के लिए खुला छोड़ देता है theArrayजो कॉल बकाया होने पर बदल जाता है ...)

सूचना है कि हम कैसे उपयोग करने indexसे forEachमें परिणाम को बचाने के लिए resultsप्रविष्टि यह से संबंधित है के रूप में एक ही स्थिति में है, भले ही परिणाम के आदेश से बाहर आने (async कॉल क्रम में जरूरी पूरा नहीं करते हैं, क्योंकि वे शुरू किए गए, जिसमें)।

लेकिन क्या होगा अगर आपको एक फ़ंक्शन से उन परिणामों को वापस करने की आवश्यकता है ? जैसा कि अन्य उत्तरों ने बताया है, आप नहीं कर सकते हैं; आपको अपना फ़ंक्शन स्वीकार करना होगा और कॉलबैक (या एक वादा लौटाना ) करना होगा। यहाँ एक कॉलबैक संस्करण है:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

उदाहरण:

या यहाँ एक संस्करण के Promiseबजाय एक वापसी है :

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

बेशक, अगर doSomethingAsyncहमसे त्रुटियां हुईं, तो rejectत्रुटि मिलने पर हम वादे को खारिज कर देंगे।)

उदाहरण:

(या वैकल्पिक रूप से, आप doSomethingAsyncउस वादे को पूरा करने के लिए एक रैपर बना सकते हैं , और फिर नीचे ...)

यदि doSomethingAsyncआप एक वादा देता है , तो आप उपयोग कर सकते हैं Promise.all:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry);
    }));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

यदि आप जानते हैं कि doSomethingAsyncएक दूसरे और तीसरे तर्क को नजरअंदाज किया जाएगा, तो आप इसे सीधे पास कर सकते हैं map( mapइसके कॉलबैक को तीन तर्कों के साथ, लेकिन ज्यादातर लोग केवल पहले समय का उपयोग करते हैं):

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

उदाहरण:

ध्यान दें कि Promise.allअपने वादे के सभी परिणामों के एक सरणी के साथ अपने वादे को हल करता है, जब वे सभी हल हो जाते हैं, या अपने वादे को अस्वीकार कर देते हैं जब आप पहले दिए गए वादों को अस्वीकार कर देते हैं।

श्रृंखला

मान लीजिए कि आप नहीं चाहते कि ऑपरेशन समानांतर हो? यदि आप उन्हें एक के बाद एक चलाना चाहते हैं, तो आपको अगला शुरू करने से पहले प्रत्येक ऑपरेशन के पूरा होने की प्रतीक्षा करनी होगी। यहां एक फ़ंक्शन का उदाहरण दिया गया है जो परिणाम के साथ कॉलबैक और कॉल करता है:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

(जब से हम श्रृंखला में काम कर रहे हैं, हम बस उपयोग कर सकते हैं results.push(result)क्योंकि हम जानते हैं कि हमें परिणाम नहीं मिलेंगे। उपरोक्त में हम उपयोग कर सकते हैं results[index] = result;, लेकिन निम्नलिखित कुछ उदाहरणों में हमारे पास कोई इंडेक्स नहीं है। उपयोग करने के लिए।)

उदाहरण:

(या, फिर से, उसके लिए एक आवरण का निर्माण करें जो doSomethingAsyncआपको एक वादा देता है और नीचे ...)

यदि doSomethingAsyncआप एक वादा देता है, यदि आप ES2017 + वाक्य रचना (शायद की तरह एक transpiler साथ उपयोग कर सकते हैं कोलाहल ), आप एक का उपयोग कर सकते asyncसमारोह के साथ for-ofऔर await:

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

उदाहरण:

यदि आप ES2017 + सिंटैक्स (अभी तक) का उपयोग नहीं कर सकते हैं, तो आप "वादा कम करें" पैटर्न पर भिन्नता का उपयोग कर सकते हैं (यह सामान्य से कम जटिल से अधिक जटिल है क्योंकि हम परिणाम को अगले में एक से नहीं दे रहे हैं, लेकिन इसके बजाय एक सरणी में उनके परिणामों को इकट्ठा करना):

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

उदाहरण:

... जो ES2015 + तीर कार्यों के साथ कम बोझिल है :

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

उदाहरण:


1
क्या आप बता सकते हैं if (--expecting === 0)कि कोड का हिस्सा कैसे काम करता है? आपके समाधान का कॉलबैक संस्करण मेरे लिए बहुत अच्छा काम कर रहा है, मुझे समझ में नहीं आता कि कैसे, उस कथन के साथ, आप पूर्ण प्रतिक्रियाओं की संख्या की जांच कर रहे हैं। सराहना करते हैं कि यह सिर्फ मेरी ओर से ज्ञान की कमी है। क्या कोई वैकल्पिक तरीका है कि जांच लिखी जा सकती है?
सारा

@ सार: expectingके मूल्य के साथ शुरू होता है array.length, जो कि हम कितने अनुरोध करने जा रहे हैं। हमें पता है कि जब तक उन सभी अनुरोधों को शुरू नहीं किया जाएगा, कॉलबैक को नहीं बुलाया जाएगा। कॉलबैक में, if (--expecting === 0)यह करता है: 1. कमी expecting(हमें एक प्रतिक्रिया मिली है, इसलिए हम एक कम प्रतिक्रिया की उम्मीद कर रहे हैं) और यदि वेतन वृद्धि के बाद का मान 0 है (हम किसी और प्रतिक्रिया की उम्मीद नहीं कर रहे हैं), हम किया हुआ!
टीजे क्राउडर

1
@PatrickRoberts - धन्यवाद !! हां, कॉपी-एंड-पेस्ट त्रुटि, उस उदाहरण में दूसरे तर्क को पूरी तरह से नजरअंदाज कर दिया गया था (जो एकमात्र कारण है कि यह विफल नहीं हुआ, क्योंकि आपने बताया था, resultsमौजूद नहीं था)। :-) ठीक कर दिया।
टीजे क्राउडर

111

इस उदाहरण पर एक नज़र डालें:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope,$http) {

    var getJoke = function(){
        return $http.get('http://api.icndb.com/jokes/random').then(function(res){
            return res.data.value;  
        });
    }

    getJoke().then(function(res) {
        console.log(res.joke);
    });
});

जैसा कि आप देख सकते हैं getJokeकि एक हल किया हुआ वादा वापस आ रहा है (यह लौटने पर हल हो गया है res.data.value)। इसलिए आप तब तक प्रतीक्षा करते हैं जब तक $ http.get अनुरोध पूरा नहीं हो जाता है और फिर कंसोल .log (res.joke) निष्पादित हो जाता है (एक सामान्य एसिंक्रोनस प्रवाह के रूप में)।

यह तख्ती है:

http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/

ES6 रास्ता (async - प्रतीक्षा)

(function(){
  async function getJoke(){
    let response = await fetch('http://api.icndb.com/jokes/random');
    let data = await response.json();
    return data.value;
  }

  getJoke().then((joke) => {
    console.log(joke);
  });
})();

107

यह उन स्थानों में से एक है जो दो तरह से डेटा बाइंडिंग या स्टोर अवधारणा है जो कई नए जावास्क्रिप्ट फ्रेमवर्क में उपयोग किया जाता है, आपके लिए बहुत अच्छा काम करेगा ...

इसलिए यदि आप Angular, React या किसी भी अन्य चौखटे का उपयोग कर रहे हैं , जो दो तरीके से डेटा बाइंडिंग या स्टोर कॉन्सेप्ट करता है, तो यह समस्या केवल आपके लिए निर्धारित है, इसलिए आसान शब्दों में, आपका परिणाम undefinedपहले चरण में है, इसलिए result = undefinedआपको प्राप्त करने से पहले आपको प्राप्त करना होगा। डेटा, फिर जैसे ही आप परिणाम प्राप्त करते हैं, इसे अपडेट किया जाएगा और नए मूल्य पर असाइन किया जाएगा जो आपके अजाक्स कॉल की प्रतिक्रिया ...

लेकिन आप इसे उदाहरण के लिए शुद्ध जावास्क्रिप्ट या jQuery में कैसे कर सकते हैं जैसा कि आपने इस प्रश्न में पूछा है?

आप कॉलबैक का उपयोग कर सकते हैं , वादा कर सकते हैं और आपके लिए इसे संभालने के लिए हाल ही में अवलोकन कर सकते हैं, उदाहरण के लिए वादों में हमारे पास कुछ फ़ंक्शन हैं जैसे success()या then()जिसे निष्पादित किया जाएगा जब आपका डेटा आपके लिए तैयार हो, तो कॉलबैक के साथ या अवलोकन योग्य फ़ंक्शन को सब्सक्राइब करें

उदाहरण के लिए आपके मामले में जो आप jQuery का उपयोग कर रहे हैं , आप कुछ इस तरह से कर सकते हैं:

$(document).ready(function(){
    function foo() {
        $.ajax({url: "api/data", success: function(data){
            fooDone(data); //after we have data, we pass it to fooDone
        }});
    };

    function fooDone(data) {
        console.log(data); //fooDone has the data and console.log it
    };

    foo(); //call happens here
});

अधिक जानकारी के लिए वादों और वेधशालाओं के बारे में अध्ययन करें, जो इस एसिंक्स सामान को करने के नए तरीके हैं।


यह वैश्विक दायरे में ठीक है, लेकिन कुछ मॉड्यूल संदर्भ में आप कॉलबैक के लिए सही संदर्भ सुनिश्चित करना चाहते हैं जैसे$.ajax({url: "api/data", success: fooDone.bind(this)});
steve.sims

8
यह वास्तव में गलत है क्योंकि रिएक्ट एक तरफा डेटा बाइंडिंग है
मैथ्यू ब्रेंट

@MatthewBrent आप गलत नहीं हैं, लेकिन सही भी नहीं हैं, रिएक्ट प्रॉप्स ऑब्जेक्ट हैं और अगर बदले गए, तो वे पूरे एप्लिकेशन में बदल जाते हैं, लेकिन इसका ऐसा तरीका नहीं है कि रिएक्टर डेवलपर इसे इस्तेमाल करने की सलाह दे ...
एलिरेज़ा

98

यह एक बहुत ही आम मुद्दा है जिसका सामना हम जावास्क्रिप्ट के 'रहस्यों' से करते हुए करते हैं। मुझे आज इस रहस्य को ध्वस्त करने की कोशिश करें।

चलो एक सरल जावास्क्रिप्ट फ़ंक्शन के साथ शुरू करते हैं:

function foo(){
// do something 
 return 'wohoo';
}

let bar = foo(); // bar is 'wohoo' here

यह एक सरल सिंक्रोनस फंक्शन कॉल है (जहां अनुक्रम की प्रत्येक पंक्ति क्रम से अगले एक से पहले before अपनी नौकरी से समाप्त हो जाती है ’, और परिणाम अपेक्षा के अनुरूप है।

अब हमारे फंक्शन में थोड़ी देरी का परिचय देते हुए थोड़ा ट्विस्ट जोड़ते हैं, ताकि कोड की सभी पंक्तियाँ क्रमानुसार 'समाप्त' न हों। इस प्रकार, यह फ़ंक्शन के अतुल्यकालिक व्यवहार का अनुकरण करेगा:

function foo(){
 setTimeout( ()=>{
   return 'wohoo';
  }, 1000 )
}

let bar = foo() // bar is undefined here

तो आप वहाँ जाते हैं, उस देरी ने हमारे द्वारा अपेक्षित कार्यक्षमता को तोड़ दिया! लेकिन वास्तव में क्या हुआ? यदि आप कोड को देखते हैं तो यह वास्तव में बहुत तार्किक है। कार्य foo(), निष्पादन पर, कुछ भी नहीं देता (इस प्रकार लौटाया गया मान है undefined), लेकिन यह एक टाइमर शुरू करता है, जो 'वोह' को वापस करने के लिए 1s के बाद एक फ़ंक्शन निष्पादित करता है। लेकिन जैसा कि आप देख सकते हैं, बार को सौंपा गया मान फू () से तुरंत लौटा हुआ सामान है, जो कि कुछ भी नहीं है undefined

तो, हम इस मुद्दे से कैसे निपटेंगे?

आइए एक PROMISE के लिए हमारे फ़ंक्शन से पूछें । वादा वास्तव में इसका क्या मतलब है: इसका मतलब है कि फ़ंक्शन आपको भविष्य में मिलने वाले किसी भी आउटपुट के साथ प्रदान करने की गारंटी देता है। तो चलिए इसे ऊपर की हमारी छोटी समस्या के लिए देखते हैं:

function foo(){
   return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
    setTimeout ( function(){ 
      // promise is RESOLVED , when execution reaches this line of code
       resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
    }, 1000 )
  })
}

let bar ; 
foo().then( res => {
 bar = res;
 console.log(bar) // will print 'wohoo'
});

इस प्रकार, सारांश यह है - अजाक्स आधारित कार्यों जैसे कि अजाक्स आधारित कॉल आदि से निपटने के लिए, आप resolveमूल्य के लिए एक वादे का उपयोग कर सकते हैं (जिसे आप वापस करने का इरादा रखते हैं)। इस प्रकार, संक्षेप में आप को हल करने के बजाय मूल्य लौटने , अतुल्यकालिक कार्यों में।

अद्यतन (async / प्रतीक्षा के साथ वादा करता है)

then/catchवादों के साथ काम करने का उपयोग करने के अलावा , एक और दृष्टिकोण मौजूद है। विचार एक अतुल्यकालिक फ़ंक्शन को पहचानना है और फिर कोड की अगली पंक्ति में जाने से पहले, वादों को हल करने के लिए प्रतीक्षा करना है । यह अभी भी केवल promisesहुड के नीचे है, लेकिन एक अलग वाक्यविन्यास दृष्टिकोण के साथ। चीजों को स्पष्ट करने के लिए, आप नीचे एक तुलना पा सकते हैं:

तब / पकड़ संस्करण:

function saveUsers(){
     getUsers()
      .then(users => {
         saveSomewhere(users);
      })
      .catch(err => {
          console.error(err);
       })
 }

async / प्रतीक्षा संस्करण:

  async function saveUsers(){
     try{
        let users = await getUsers()
        saveSomewhere(users);
     }
     catch(err){
        console.error(err);
     }
  }

क्या यह अभी भी एक वादा या async / प्रतीक्षा से मूल्य वापस करने का सबसे अच्छा तरीका माना जाता है?
20

3
@edwardsmarkf व्यक्तिगत रूप से मुझे नहीं लगता कि इस तरह का एक सबसे अच्छा तरीका है। मैं अपने कोड के async भागों के लिए वादे / कैच, async / प्रतीक्षा के साथ-साथ जनरेटर का उपयोग करता हूं। यह काफी हद तक उपयोग के संदर्भ पर निर्भर करता है।
अनिश के।

96

एक एसिंक्रोनस फ़ंक्शन से मान वापस करने के लिए एक और दृष्टिकोण, एक ऑब्जेक्ट में पास करना है जो परिणाम को एसिंक्रोनस फ़ंक्शन से संग्रहीत करेगा।

यहाँ उसी का एक उदाहरण दिया गया है:

var async = require("async");

// This wires up result back to the caller
var result = {};
var asyncTasks = [];
asyncTasks.push(function(_callback){
    // some asynchronous operation
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;
            _callback();
        }
    });
});

async.parallel(asyncTasks, function(){
    // result is available after performing asynchronous operation
    console.log(result)
    console.log('Done');
});

मैं resultअतुल्यकालिक ऑपरेशन के दौरान मूल्य को स्टोर करने के लिए ऑब्जेक्ट का उपयोग कर रहा हूं । यह परिणाम को अतुल्यकालिक नौकरी के बाद भी उपलब्ध होने की अनुमति देता है।

मैं इस दृष्टिकोण का उपयोग करता हूं। मुझे यह जानने में रुचि होगी कि यह दृष्टिकोण कितनी अच्छी तरह काम करता है जहां लगातार मॉड्यूल के माध्यम से परिणाम को वापस लाना शामिल है।


9
यहां किसी वस्तु का उपयोग करने के बारे में कुछ खास नहीं है। यह भी काम करेगा अगर आपने उसे सीधे जवाब दिया result। यह काम करता है क्योंकि आप async फ़ंक्शन पूरा होने के बाद चर पढ़ रहे हैं ।
फेलिक्स क्लिंग

85

हालांकि वादे और कॉलबैक कई स्थितियों में ठीक काम करते हैं, यह कुछ इस तरह व्यक्त करने के लिए पीछे का दर्द है:

if (!name) {
  name = async1();
}
async2(name);

आप अंत तक जाना चाहते हैं async1; जाँच करें कि nameक्या अपरिभाषित है या नहीं और तदनुसार कॉलबैक कॉल करें।

async1(name, callback) {
  if (name)
    callback(name)
  else {
    doSomething(callback)
  }
}

async1(name, async2)

जबकि छोटे उदाहरणों में यह ठीक है , जब आपको बहुत सारे ऐसे ही मामले और त्रुटि से निपटने में गुस्सा आता है।

Fibers समस्या को हल करने में मदद करता है।

var Fiber = require('fibers')

function async1(container) {
  var current = Fiber.current
  var result
  doSomething(function(name) {
    result = name
    fiber.run()
  })
  Fiber.yield()
  return result
}

Fiber(function() {
  var name
  if (!name) {
    name = async1()
  }
  async2(name)
  // Make any number of async calls from here
}

आप यहां प्रोजेक्ट चेकआउट कर सकते हैं


1
@recurf - यह मेरी परियोजना नहीं है। आप उनकी समस्या ट्रैकर का उपयोग करके देख सकते हैं।
15:20

1
क्या यह जनरेटर के कार्यों के समान है? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… *
Emanegux

1
क्या यह अभी भी प्रासंगिक है?
एलुआन हदद

async-awaitयदि आप नोड के कुछ नए संस्करणों का उपयोग कर रहे हैं, तो आप इसका उपयोग कर सकते हैं । यदि कोई पुराने संस्करणों के साथ फंस गया है तो वे इस विधि का उपयोग कर सकते हैं।
रोहिथप्र

83

निम्नलिखित उदाहरण मैंने लिखा है कि कैसे

  • एसिंक्रोनस HTTP कॉल्स को हैंडल करें;
  • प्रत्येक एपीआई कॉल से प्रतिक्रिया की प्रतीक्षा करें;
  • प्रॉमिस पैटर्न का उपयोग करें ;
  • कई HTTP कॉल में शामिल होने के लिए Promise.all पैटर्न का उपयोग करें ;

यह काम करने का उदाहरण आत्म-निहित है। यह एक साधारण अनुरोध ऑब्जेक्ट को परिभाषित करेगा XMLHttpRequestजो कॉल करने के लिए विंडो ऑब्जेक्ट का उपयोग करता है । वादों का एक गुच्छा पूरा होने के लिए प्रतीक्षा करने के लिए यह एक सरल कार्य को परिभाषित करेगा।

प्रसंग। क्वेरी स्ट्रिंग्स के दिए गए सेट के लिए ऑब्जेक्ट खोजने के लिए उदाहरण Spotify Web API एंडपॉइंट playlistको क्वेरी कर रहा है:

[
 "search?type=playlist&q=%22doom%20metal%22",
 "search?type=playlist&q=Adele"
]

प्रत्येक आइटम के लिए, एक नया वादा एक ब्लॉक को आग देगा - ExecutionBlockपरिणाम को पार्स करें, परिणाम सरणी के आधार पर वादों के एक नए सेट को शेड्यूल करें, जो कि Spotify userऑब्जेक्ट्स की एक सूची है और ExecutionProfileBlockअतुल्यकालिक रूप से नए HTTP कॉल को निष्पादित करें ।

फिर आप एक नेस्टेड प्रोमिस संरचना देख सकते हैं, जो आपको एकाधिक और पूरी तरह से अतुल्यकालिक नेस्टेड HTTP कॉल को स्पॉन करने देता है, और कॉल के प्रत्येक सबसेट से परिणामों में शामिल होता है Promise.all

नोट हाल ही में Spotify searchAPI को अनुरोध शीर्षलेख में निर्दिष्ट किए जाने के लिए एक पहुंच टोकन की आवश्यकता होगी:

-H "Authorization: Bearer {your access token}" 

इसलिए, आपको निम्नलिखित उदाहरणों को चलाने के लिए आपको अनुरोध हेडर में अपना एक्सेस टोकन डालना होगा:

var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
    log: function(s) {
        document.getElementById("console").innerHTML += s + "<br/>"
    }
}

// Simple XMLHttpRequest
// based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
        var request;
        if (window.XMLHttpRequest) { // Mozilla, Safari, ...
            request = new XMLHttpRequest();
        } else if (window.ActiveXObject) { // Internet Explorer
            try {
                request = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch (e) {
                try {
                  request = new ActiveXObject('Microsoft.XMLHTTP');
                } catch (e) {}
            }
        }

        // State changes
        request.onreadystatechange = function() {
            if (request.readyState === 4) { // Done
                if (request.status === 200) { // Complete
                    response(request.responseText)
                }
                else
                    response();
            }
        }
        request.open('GET', what, true);
        request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
        request.send(null);
    }
}

//PromiseAll
var promiseAll = function(items, block, done, fail) {
    var self = this;
    var promises = [],
                   index = 0;
    items.forEach(function(item) {
        promises.push(function(item, i) {
            return new Promise(function(resolve, reject) {
                if (block) {
                    block.apply(this, [item, index, resolve, reject]);
                }
            });
        }(item, ++index))
    });
    Promise.all(promises).then(function AcceptHandler(results) {
        if (done) done(results);
    }, function ErrorHandler(error) {
        if (fail) fail(error);
    });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
    var url = "https://api.spotify.com/v1/"
    url += item;
    console.log( url )
    SimpleRequest.call(url, function(result) {
        if (result) {

            var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
                return item.owner.href;
            })
            resolve(profileUrls);
        }
        else {
            reject(new Error("call error"));
        }
    })
}

arr = [
    "search?type=playlist&q=%22doom%20metal%22",
    "search?type=playlist&q=Adele"
]

promiseAll(arr, function(item, index, resolve, reject) {
    console.log("Making request [" + index + "]")
    ExecutionBlock(item, index, resolve, reject);
}, function(results) { // Aggregated results

    console.log("All profiles received " + results.length);
    //console.log(JSON.stringify(results[0], null, 2));

    ///// promiseall again

    var ExecutionProfileBlock = function(item, index, resolve, reject) {
        SimpleRequest.call(item, function(result) {
            if (result) {
                var obj = JSON.parse(result);
                resolve({
                    name: obj.display_name,
                    followers: obj.followers.total,
                    url: obj.href
                });
            } //result
        })
    } //ExecutionProfileBlock

    promiseAll(results[0], function(item, index, resolve, reject) {
        //console.log("Making request [" + index + "] " + item)
        ExecutionProfileBlock(item, index, resolve, reject);
    }, function(results) { // aggregated results
        console.log("All response received " + results.length);
        console.log(JSON.stringify(results, null, 2));
    }

    , function(error) { // Error
        console.log(error);
    })

    /////

  },
  function(error) { // Error
      console.log(error);
  });
<div id="console" />

मैंने इस समाधान पर यहां व्यापक चर्चा की है


80

संक्षिप्त उत्तर है, आपको इस तरह से कॉलबैक लागू करना होगा:

function callback(response) {
    // Here you can do what ever you want with the response object.
    console.log(response);
}

$.ajax({
    url: "...",
    success: callback
});

78

2017 का उत्तर: आप अब वही कर सकते हैं जो आप हर वर्तमान ब्राउज़र और नोड में चाहते हैं

यह काफी सरल है:

  • एक वादा वापस करो
  • 'प्रतीक्षा' का उपयोग करें , जो जावास्क्रिप्ट को मूल्य में हल किए जाने वाले वादे का इंतजार करने के लिए बताएगा (जैसे HTTP प्रतिक्रिया)
  • मूल फ़ंक्शन में 'async' कीवर्ड जोड़ें

यहां आपके कोड का एक कार्यशील संस्करण है:

(async function(){

var response = await superagent.get('...')
console.log(response)

})()

प्रतीक्षा को सभी वर्तमान ब्राउज़रों और नोड 8 में समर्थित है


7
दुर्भाग्य से, यह केवल उन कार्यों के साथ काम करता है जो वादे वापस करते हैं - उदाहरण के लिए यह Node.js API के साथ काम नहीं करता है, जो कॉलबैक का उपयोग करता है। और मैं इसे बेबल के बिना उपयोग करने की सलाह नहीं दूंगा, क्योंकि हर कोई "वर्तमान ब्राउज़रों" का उपयोग नहीं करता है।
मिशैल पेरोलाकोव्स्की

2
@ MichałPerłakowski नोड 8 में nodejs.org/api/util.html#util_util_promisify_original शामिल हैं जिनका उपयोग नोड.जेएस एपीआई रिटर्न वादे करने के लिए किया जा सकता है। चाहे आपके पास गैर-वर्तमान ब्राउज़रों का समर्थन करने का समय और पैसा है या नहीं, यह स्पष्ट रूप से आपकी स्थिति पर निर्भर करता है।
मिकमेकाना

IE 11 अभी भी 2018 में एक वर्तमान ब्राउज़र है, दुख की बात है और यह समर्थन नहीं करता हैawait/async
जुआन मेंडेस

IE11 एक वर्तमान ब्राउज़र नहीं है। यह 5 साल पहले जारी किया गया था, कैनिएज़ के अनुसार दुनिया भर में बाजार में हिस्सेदारी 2.5% है, और जब तक कोई आपके बजट को दोगुना नहीं कर रहा है, तब तक सभी मौजूदा तकनीक को अनदेखा नहीं कर सकते हैं।
मिकमेकाना

76

Js एक एकल पिरोया हुआ है।

ब्राउज़र को तीन भागों में विभाजित किया जा सकता है:

1) इवेंट लूप

2) वेब एपीआई

3) इवेंट कतार

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

अब हमें लगता है कि हमने एक कतार में दो कार्यों को धकेल दिया है एक सर्वर से डेटा प्राप्त करने के लिए है और दूसरा उस डेटा का उपयोग करता है। हमने पहले सर्वर का उपयोग किया। serverRequest फ़ंक्शन ईवेंट लूप में जाता है और सर्वर को कॉल करता है क्योंकि हमें कभी नहीं पता होता है कि सर्वर से डेटा प्राप्त करने में कितना समय लगेगा, इसलिए इस प्रक्रिया में समय लगने की उम्मीद है और इसलिए हम अपने ईवेंट लूप में व्यस्त रहते हैं, इस प्रकार हमारा पेज हैंग हो जाता है, जहां वेब है API भूमिका में आता है और यह फ़ंक्शन लूप से लेता है और सर्वर से इवेंट लूप फ्री में डील करता है ताकि हम कतार से अगले फ़ंक्शन को निष्पादित कर सकें। कतार में अगला फ़ंक्शन उपयोग है () जो लूप में जाता है लेकिन कोई डेटा उपलब्ध नहीं होने के कारण अगले समारोह की बर्बादी और निष्पादन कतार के अंत तक जारी रहता है। (इसे Async कॉलिंग कहा जाता है। हम डेटा प्राप्त करने तक कुछ और कर सकते हैं)

मान लें कि हमारे serverRequest () फ़ंक्शन का एक कोड में रिटर्न स्टेटमेंट था, जब हमें सर्वर से डेटा वापस मिलता है तो वेब एपीआई इसे कतार के अंत में कतार में धकेल देगा। चूंकि यह कतार में अंत में धकेल दिया जाता है इसलिए हम इसके डेटा का उपयोग नहीं कर सकते क्योंकि इस डेटा का उपयोग करने के लिए हमारी कतार में कोई फ़ंक्शन नहीं बचा है। इस प्रकार Async Call से कुछ वापस करना संभव नहीं है।

इस प्रकार इसका समाधान कॉलबैक या वादा है

यहाँ एक उत्तर से एक छवि, सही ढंग से कॉलबैक उपयोग की व्याख्या करती है ... हम अपने कार्य (फ़ंक्शन का उपयोग सर्वर से लौटाए गए डेटा) को कॉलिंग सर्वर को कार्य करने के लिए करते हैं।

वापस कॉल करें

 function doAjax(callbackFunc, method, url) {
  var xmlHttpReq = new XMLHttpRequest();
  xmlHttpReq.open(method, url);
  xmlHttpReq.onreadystatechange = function() {

      if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) {
        callbackFunc(xmlHttpReq.responseText);
      }


  }
  xmlHttpReq.send(null);

}

मेरे कोड में इसे कहा जाता है

function loadMyJson(categoryValue){
  if(categoryValue==="veg")
  doAjax(print,"GET","http://localhost:3004/vegetables");
  else if(categoryValue==="fruits")
  doAjax(print,"GET","http://localhost:3004/fruits");
  else 
  console.log("Data not found");
}

Javscript.info कॉलबैक


68

आप रिमोट कॉल करने के लिए इस कस्टम लाइब्रेरी (प्रोमिस का उपयोग करते हुए लिखे गए) का उपयोग कर सकते हैं।

function $http(apiConfig) {
    return new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open(apiConfig.method, apiConfig.url);
        client.send();
        client.onload = function () {
            if (this.status >= 200 && this.status < 300) {
                // Performs the function "resolve" when this.status is equal to 2xx.
                // Your logic here.
                resolve(this.response);
            }
            else {
                // Performs the function "reject" when this.status is different than 2xx.
                reject(this.statusText);
            }
        };
        client.onerror = function () {
            reject(this.statusText);
        };
    });
}

सरल उपयोग उदाहरण:

$http({
    method: 'get',
    url: 'google.com'
}).then(function(response) {
    console.log(response);
}, function(error) {
    console.log(error)
});

67

एक अन्य समाधान अनुक्रमिक निष्पादक nsynjs के माध्यम से कोड निष्पादित करना है

यदि अंतर्निहित फ़ंक्शन का वादा किया जाता है

nsynjs क्रमिक रूप से सभी वादों का मूल्यांकन करेंगे, और dataसंपत्ति में वादा परिणाम डालेंगे :

function synchronousCode() {

    var getURL = function(url) {
        return window.fetch(url).data.text().data;
    };
    
    var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
    console.log('received bytes:',getURL(url).length);
    
};

nsynjs.run(synchronousCode,{},function(){
    console.log('synchronousCode done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>

यदि अंतर्निहित फ़ंक्शन का वादा नहीं किया जाता है

चरण 1. nsynjs- जागरूक आवरण में कॉलबैक के साथ लपेटें फ़ंक्शन (यदि यह प्रोमिसिफाइड संस्करण है, तो आप इस चरण को छोड़ सकते हैं):

var ajaxGet = function (ctx,url) {
    var res = {};
    var ex;
    $.ajax(url)
    .done(function (data) {
        res.data = data;
    })
    .fail(function(e) {
        ex = e;
    })
    .always(function() {
        ctx.resume(ex);
    });
    return res;
};
ajaxGet.nsynjsHasCallback = true;

चरण 2. फ़ंक्शन में सिंक्रोनस लॉजिक रखें:

function process() {
    console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}

चरण 3. nsynjs के माध्यम से समकालिक तरीके से कार्य करें:

nsynjs.run(process,this,function () {
    console.log("synchronous function finished");
});

अगर कुछ धीमी गति से कार्य करने का परिणाम तैयार नहीं है, तो एनएसजीएनजे सभी ऑपरेटरों और भावों का मूल्यांकन करेगा।

यहाँ और अधिक उदाहरण: https://github.com/amaksr/nsynjs/tree/master/examples


2
यह दिलचस्प है। मुझे पसंद है कि यह कैसे async कोड को अनुमति देता है जिस तरह से आप इसे अन्य भाषाओं में करेंगे। लेकिन तकनीकी रूप से यह वास्तविक जावास्क्रिप्ट नहीं है?
जे मॉरिस

41

ECMAScript 6 में 'जनरेटर' हैं जो आपको एक अतुल्यकालिक शैली में आसानी से प्रोग्राम करने की अनुमति देते हैं।

function* myGenerator() {
    const callback = yield;
    let [response] = yield $.ajax("https://stackoverflow.com", {complete: callback});
    console.log("response is:", response);

    // examples of other things you can do
    yield setTimeout(callback, 1000);
    console.log("it delayed for 1000ms");
    while (response.statusText === "error") {
        [response] = yield* anotherGenerator();
    }
}

उपरोक्त कोड चलाने के लिए आप ऐसा करें:

const gen = myGenerator(); // Create generator
gen.next(); // Start it
gen.next((...args) => gen.next([...args])); // Set its callback function

यदि आपको उन ब्राउज़रों को लक्षित करने की आवश्यकता है जो ईएस 6 का समर्थन नहीं करते हैं तो आप ईसीएमएस्क्रिप्ट 5 उत्पन्न करने के लिए बैबेल या क्लोजर-कंपाइलर के माध्यम से कोड चला सकते हैं।

कॉलबैक ...argsको एक सरणी में लपेटा जाता है और जब आप उन्हें पढ़ते हैं तो विनाशकारी हो जाता है ताकि पैटर्न कॉलबैक से सामना कर सके जिसमें कई तर्क होते हैं। नोड एफएस के साथ उदाहरण के लिए :

const [err, data] = yield fs.readFile(filePath, "utf-8", callback);

39

अतुल्यकालिक अनुरोधों के साथ काम करने के लिए यहां कुछ दृष्टिकोण दिए गए हैं:

  1. ब्राउज़र वादा वस्तु
  2. क्यू - जावास्क्रिप्ट के लिए एक वादा पुस्तकालय
  3. A + वादा करता है। Js
  4. jQuery के आस्थगित
  5. XMLHttpRequest API
  6. कॉलबैक अवधारणा का उपयोग करना - पहले उत्तर में कार्यान्वयन के रूप में

उदाहरण: कई अनुरोधों के साथ काम करने के लिए jQuery के आस्थगित कार्यान्वयन

var App = App || {};

App = {
    getDataFromServer: function(){

      var self = this,
                 deferred = $.Deferred(),
                 requests = [];

      requests.push($.getJSON('request/ajax/url/1'));
      requests.push($.getJSON('request/ajax/url/2'));

      $.when.apply(jQuery, requests).done(function(xhrResponse) {
        return deferred.resolve(xhrResponse.result);
      });
      return deferred;
    },

    init: function(){

        this.getDataFromServer().done(_.bind(function(resp1, resp2) {

           // Do the operations which you wanted to do when you
           // get a response from Ajax, for example, log response.
        }, this));
    }
};
App.init();


38

हम खुद को एक ऐसे ब्रह्मांड में पाते हैं जो एक आयाम के साथ प्रगति करता है जिसे हम "समय" कहते हैं। हम वास्तव में नहीं समझते हैं कि समय क्या है, लेकिन हमने अमूर्तता और शब्दावली विकसित की है जो हमें इसका कारण और बात करते हैं: "अतीत", "वर्तमान", "भविष्य", "पहले", "बाद"।

कंप्यूटर सिस्टम जो हम बनाते हैं - अधिक से अधिक - एक महत्वपूर्ण आयाम के रूप में समय है। भविष्य में होने के लिए कुछ चीजें निर्धारित की जाती हैं। फिर अन्य चीजों को पहले उन चीजों के बाद होने की आवश्यकता होती है जो अंततः होती हैं। यह "एसिंक्रोनसिटी" नामक मूल धारणा है। हमारी बढ़ती नेटवर्क दुनिया में, अतुल्यकालिकता का सबसे आम मामला कुछ अनुरोधों के जवाब के लिए कुछ दूरस्थ प्रणाली की प्रतीक्षा कर रहा है।

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

var milk = order_milk();
put_in_coffee(milk);

क्योंकि जे एस कोई रास्ता नहीं पता है कि यह करने की जरूरत है इंतज़ार के लिए order_milkइससे पहले कि यह कार्यान्वित समाप्त करने के लिए put_in_coffee। दूसरे शब्दों में, यह पता नहीं है कि order_milkहै अतुल्यकालिक --is कुछ है कि कुछ भविष्य समय तक दूध में परिणाम के लिए नहीं जा रहा है। जेएस, और अन्य घोषणात्मक भाषाएं प्रतीक्षा किए बिना एक के बाद एक बयान निष्पादित करती हैं।

इस समस्या के बारे में क्लासिक जेएस दृष्टिकोण, इस तथ्य का लाभ उठाते हुए कि जेएस प्रथम श्रेणी की वस्तुओं के रूप में कार्यों का समर्थन करता है जो कि पास हो सकता है, अतुल्यकालिक अनुरोध के पैरामीटर के रूप में एक फ़ंक्शन पास करना है, जो तब पूरा हो जाएगा जब यह पूरा हो जाएगा। भविष्य में कभी-कभी इसका कार्य। यह "कॉलबैक" दृष्टिकोण है। यह इस तरह दिख रहा है:

order_milk(put_in_coffee);

order_milk बंद करता है, दूध का आदेश देता है, तब, जब और केवल जब यह आता है, यह आह्वान करता है put_in_coffee

इस कॉलबैक दृष्टिकोण के साथ समस्या यह है कि यह किसी फ़ंक्शन के सामान्य शब्दार्थ को प्रदूषित करता है, जिसके परिणाम की रिपोर्ट करता है return; इसके बजाय, फ़ंक्शंस को पैरामीटर के रूप में दिए गए कॉलबैक पर कॉल करके अपने परिणामों की रिपोर्ट नहीं करनी चाहिए। इसके अलावा, घटनाओं के लंबे दृश्यों के साथ काम करते समय यह दृष्टिकोण तेजी से कम हो सकता है। उदाहरण के लिए, मान लें कि मैं कॉफी में दूध डालने का इंतजार करना चाहता हूं, और उसके बाद ही कॉफी पीने के बाद तीसरा कदम उठाता हूं। मुझे इस तरह से कुछ लिखने की आवश्यकता है:

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

जहाँ मैं put_in_coffeeदूध डालने के लिए दोनों को पास कर रहा हूँ , और कार्रवाई भी (drink_coffee दूध डालने के बाद एक बार निष्पादित करने ) । ऐसा कोड लिखना, और पढ़ना और डीबग करना कठिन हो जाता है।

इस मामले में, हम प्रश्न में कोड को फिर से लिख सकते हैं:

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

वादे दर्ज करें

यह एक "वादा" की धारणा के लिए प्रेरणा थी, जो एक विशेष प्रकार का मूल्य है जो किसी प्रकार के भविष्य या अतुल्यकालिक परिणाम का प्रतिनिधित्व करता है । यह उस चीज का प्रतिनिधित्व कर सकता है जो पहले से ही हुआ है, या जो भविष्य में होने वाला है, या कभी भी ऐसा नहीं हो सकता है। वादों की एक एकल विधि है, जिसका नाम है then, जिसमें आप एक कार्रवाई को निष्पादित करते हैं जब वादे का प्रतिनिधित्व किया गया परिणाम का एहसास हो जाता है।

हमारे दूध और कॉफी के मामले में, हम order_milkदूध के आगमन का वादा वापस करने के लिए डिज़ाइन करते हैं, फिर put_in_coffeeएक thenकार्रवाई के रूप में निर्दिष्ट करते हैं , इस प्रकार है:

order_milk() . then(put_in_coffee)

इसका एक फायदा यह है कि हम भविष्य में होने वाली घटनाओं ("चैनिंग") के अनुक्रम बनाने के लिए इन्हें एक साथ जोड़ सकते हैं:

order_milk() . then(put_in_coffee) . then(drink_coffee)

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

function get_data() {
  return $.ajax('/foo.json');
}

दरअसल, हमने जो कुछ भी किया है returnवह कॉल टू में जोड़ा गया है $.ajax। यह काम करता है क्योंकि jQuery $.ajaxपहले से ही एक तरह का वादा जैसी चीज़ लौटाता है। (व्यवहार में, विवरण में शामिल हुए बिना, हम इस कॉल को लपेटना पसंद करेंगे ताकि एक वास्तविक वादा वापस कर सकें, या ऐसा करने के लिए कुछ विकल्प का उपयोग $.ajaxकरें।) अब, यदि हम फ़ाइल लोड करना चाहते हैं और इसके खत्म होने की प्रतीक्षा करें और तो कुछ करो, हम बस कह सकते हैं

get_data() . then(do_something)

उदाहरण के लिए,

get_data() . 
  then(function(data) { console.log(data); });

वादों का उपयोग करते समय, हम बहुत सारे कार्यों को समाप्त कर देते हैं then, इसलिए अक्सर अधिक कॉम्पैक्ट ES6- शैली तीर फ़ंक्शन का उपयोग करना उपयोगी होता है:

get_data() . 
  then(data => console.log(data));

asyncकीवर्ड

लेकिन वहाँ अभी भी कुछ अस्पष्ट रूप से कोड एक तरह से लिखने के बारे में असंतुष्ट है अगर तुल्यकालिक और एक बहुत अलग तरीका है अगर अतुल्यकालिक। तुल्यकालिक के लिए, हम लिखते हैं

a();
b();

लेकिन अगर aअतुल्यकालिक है, तो वादों के साथ हमें लिखना होगा

a() . then(b);

ऊपर, हमने कहा, "जेएस के पास यह जानने का कोई तरीका नहीं है कि दूसरे को निष्पादित करने से पहले इसे समाप्त करने के लिए पहले कॉल की प्रतीक्षा करने की आवश्यकता है "। अगर जेएस को यह बताने का कोई तरीका होता तो अच्छा नहीं होता ? यह पता चला है कि awaitएक विशेष प्रकार के फ़ंक्शन के अंदर एक "async" फ़ंक्शन नामक कीवर्ड का उपयोग किया जाता है। यह सुविधा ES के आगामी संस्करण का हिस्सा है, लेकिन पहले से ही ट्रांसपॉयलर में उपलब्ध है जैसे कि बाबेल ने सही प्रीसेट दिए हैं। यह हमें बस लिखने की अनुमति देता है

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

आपके मामले में, आप कुछ ऐसा लिख ​​सकेंगे

async function foo() {
  data = await get_data();
  console.log(data);
}

37

संक्षिप्त उत्तर : आपका foo()तरीका तुरंत वापस आ जाता है, जबकि $ajax()कॉल फ़ंक्शन वापस आने के बाद एसिंक्रोनस रूप से निष्पादित होता है । समस्या यह है कि कैसे या कहाँ async कॉल द्वारा प्राप्त परिणामों को संग्रहीत करने के लिए एक बार यह रिटर्न।

इस सूत्र में कई समाधान दिए गए हैं। शायद सबसे आसान तरीका है कि किसी ऑब्जेक्ट को foo()विधि में पास किया जाए, और उस वस्तु के एक सदस्य में परिणाम को संग्रहीत करने के लिए async कॉल पूरा होने के बाद।

function foo(result) {
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;   // Store the async result
        }
    });
}

var result = { response: null };   // Object to hold the async result
foo(result);                       // Returns before the async completes

ध्यान दें कि कॉल foo()अभी भी उपयोगी कुछ भी नहीं लौटाएगा। हालाँकि, async कॉल का परिणाम अब संग्रहीत किया जाएगा result.response


14
हालांकि यह काम करता है, यह वास्तव में एक वैश्विक चर को असाइन करने से बेहतर नहीं है।
फेलिक्स क्लिंग

36

सफलता के callback()अंदर एक फ़ंक्शन का उपयोग करें foo()। इस तरह से प्रयास करें। यह सरल और समझने में आसान है।  

var lat = "";
var lon = "";
function callback(data) {
    lat = data.lat;
    lon = data.lon;
}
function getLoc() {
    var url = "http://ip-api.com/json"
    $.getJSON(url, function(data) {
        callback(data);
    });
}

getLoc();

29

सवाल यह था:

मैं एसिंक्रोनस कॉल से प्रतिक्रिया कैसे लौटाऊं?

जिसकी व्याख्या इस प्रकार की जा सकती है:

कैसे बनाने के लिए अतुल्यकालिक कोड नज़र तुल्यकालिक ?

समाधान कॉलबैक से बचने के लिए होगा, और वादों और async / प्रतीक्षा के संयोजन का उपयोग करना होगा

मैं एक अजाक्स अनुरोध के लिए एक उदाहरण देना चाहूंगा।

(हालांकि यह जावास्क्रिप्ट में लिखा जा सकता है, मैं इसे पायथन में लिखना पसंद करता हूं, और इसे ट्रांसक्रिप्ट के लिए जावास्क्रिप्ट में संकलित करना चाहता हूं । यह पर्याप्त स्पष्ट होगा।)

पहले JQuery के उपयोग को सक्षम करें, $जैसा कि उपलब्ध है S:

__pragma__ ('alias', 'S', '$')

एक फ़ंक्शन को परिभाषित करें जो एक वादा करता है , इस मामले में एक अजाक्स कॉल:

def read(url: str):
    deferred = S.Deferred()
    S.ajax({'type': "POST", 'url': url, 'data': { },
        'success': lambda d: deferred.resolve(d),
        'error': lambda e: deferred.reject(e)
    })
    return deferred.promise()

एसिंक्रोनस कोड का उपयोग करें जैसे कि वह सिंक्रोनस था :

async def readALot():
    try:
        result1 = await read("url_1")
        result2 = await read("url_2")
    except Exception:
        console.warn("Reading a lot failed")

29

वादा का उपयोग करना

इस सवाल का सबसे सही जवाब उपयोग कर रहा है Promise

function ajax(method, url, params) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open(method, url);
    xhr.send(params);
  });
}

प्रयोग

ajax("GET", "/test", "acrive=1").then(function(result) {
    // Code depending on result
})
.catch(function() {
    // An error occurred
});

लेकिन रुकें...!

वादों का उपयोग करने में समस्या है!

हमें अपने कस्टम प्रॉमिस का उपयोग क्यों करना चाहिए?

मैं कुछ समय के लिए इस समाधान का उपयोग कर रहा था जब तक मुझे लगा कि पुराने ब्राउज़रों में कोई त्रुटि है:

Uncaught ReferenceError: Promise is not defined

इसलिए मैंने अपने कंप्लीट क्लास को ES3 के लिए js कंपाइलर से नीचे लागू करने का फैसला किया है, अगर यह परिभाषित नहीं है। बस अपने मुख्य कोड से पहले इस कोड को जोड़ें और फिर सुरक्षित रूप से प्रॉमिस का उपयोग करें!

if(typeof Promise === "undefined"){
    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) { 
            throw new TypeError("Cannot call a class as a function"); 
        }
    }
    var Promise = function () {
        function Promise(main) {
            var _this = this;
            _classCallCheck(this, Promise);
            this.value = undefined;
            this.callbacks = [];
            var resolve = function resolve(resolveValue) {
                _this.value = resolveValue;
                _this.triggerCallbacks();
            };
            var reject = function reject(rejectValue) {
                _this.value = rejectValue;
                _this.triggerCallbacks();
            };
            main(resolve, reject);
        }
        Promise.prototype.then = function then(cb) {
            var _this2 = this;
            var next = new Promise(function (resolve) {
                _this2.callbacks.push(function (x) {
                    return resolve(cb(x));
                });
            });
            return next;
        };
        Promise.prototype.catch = function catch_(cb) {
            var _this2 = this;
            var next = new Promise(function (reject) {
                _this2.callbacks.push(function (x) {
                    return reject(cb(x));
                });
            });
            return next;
        };
        Promise.prototype.triggerCallbacks = function triggerCallbacks() {
            var _this3 = this;
            this.callbacks.forEach(function (cb) {
                cb(_this3.value);
            });
        };
        return Promise;
    }();
}

28

बेशक, सिंक्रोनस अनुरोध, वादा जैसे कई दृष्टिकोण हैं, लेकिन मेरे अनुभव से मुझे लगता है कि आपको कॉलबैक दृष्टिकोण का उपयोग करना चाहिए। यह जावास्क्रिप्ट के अतुल्यकालिक व्यवहार के लिए स्वाभाविक है। तो, आपका कोड स्निपेट थोड़ा अलग लिखा जा सकता है:

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            myCallback(response);
        }
    });

    return result;
}

function myCallback(response) {
    // Does something.
}

5
कॉलबैक या जावास्क्रिप्ट के बारे में स्वाभाविक रूप से अतुल्यकालिक कुछ भी नहीं है।
एलुआन हदद

19

आप पर कोड फेंकने के बजाय, 2 अवधारणाएं हैं जो यह समझने की कुंजी हैं कि जेएस कॉलबैक और एसिंक्रोनसिटी को कैसे संभालता है। (क्या यह एक शब्द भी है?)

इवेंट लूप और कॉनसेरी मॉडल

तीन चीजों से आपको अवगत होना चाहिए; कतार; इवेंट लूप और स्टैक

व्यापक, सरलीकृत शब्दों में, ईवेंट लूप प्रोजेक्ट मैनेजर की तरह है, यह लगातार किसी भी कार्य के लिए सुन रहा है जो कतार और स्टैक के बीच चलना और संचार करना चाहता है।

while (queue.waitForMessage()) {
   queue.processNextMessage();
}

एक बार जब उसे कुछ चलाने का संदेश मिलता है तो वह उसे कतार में जोड़ता है। कतार उन चीजों की सूची है जो निष्पादित करने के लिए इंतजार कर रही हैं (जैसे आपके AJAX अनुरोध)। इसे इस तरह से कल्पना करें:

 1. call foo.com/api/bar using foobarFunc
 2. Go perform an infinite loop
 ... and so on

जब इन संदेशों में से एक को निष्पादित करने जा रहा है, यह कतार से संदेश को पॉप करता है और एक स्टैक बनाता है, स्टैक वह सब कुछ है जिसे जेएस को संदेश में निर्देश को निष्पादित करने के लिए निष्पादित करने की आवश्यकता होती है। इसलिए हमारे उदाहरण में इसे कॉल करने के लिए कहा जा रहा हैfoobarFunc

function foobarFunc (var) {
  console.log(anotherFunction(var));
}

कुछ भी है कि foobarFunc को निष्पादित करने की आवश्यकता है (हमारे मामले में) anotherFunction ) स्टैक पर धकेल दिया जाएगा। निष्पादित, और फिर के बारे में भूल गए - इवेंट लूप फिर कतार में अगली चीज़ पर ले जाएगा (या संदेशों के लिए सुनो)

यहाँ मुख्य बात निष्पादन का क्रम है। अर्थात्

जब कुछ चलने वाला है

जब आप किसी बाहरी पार्टी को AJAX का उपयोग करके कॉल करते हैं या कोई अतुल्यकालिक कोड (उदाहरण के लिए एक सेटटाइमआउट) चलाते हैं, तो आगे बढ़ने से पहले जावास्क्रिप्ट एक प्रतिक्रिया पर निर्भर होता है।

बड़ा सवाल यह है कि इसे प्रतिक्रिया कब मिलेगी? इसका उत्तर हमें नहीं पता है - इसलिए इवेंट लूप उस संदेश का इंतजार कर रहा है जब वह कहता है "अरे मुझे चला"। यदि JS सिर्फ उस संदेश के लिए आस-पास इंतजार कर रहा है, तो समकालिक रूप से आपका ऐप फ्रीज हो जाएगा और वह बेकार हो जाएगा। इसलिए जेएस कतार में अगले आइटम को क्रियान्वित करने के लिए संदेश को इंतजार करने के लिए कतार में ले जाता है।

इसलिए अतुल्यकालिक कार्यक्षमता के साथ हम कॉलबैक नामक चीजों का उपयोग करते हैं । यह काफी वादे की तरह थोड़े है । जैसा कि मैंने कुछ बिंदु पर कुछ वापस करने का वादा किया है jQuery के विशिष्ट कॉलबैक deffered.done deffered.failऔर deffered.always(अन्य के बीच) का उपयोग करता है । आप उन्हें यहाँ देख सकते हैं

तो आपको जो करने की आवश्यकता है वह एक फ़ंक्शन है जिसे उस समय पारित होने वाले डेटा के साथ किसी बिंदु पर निष्पादित करने का वादा किया जाता है।

क्योंकि कॉलबैक को तुरंत निष्पादित नहीं किया जाता है, लेकिन बाद के समय में फ़ंक्शन के संदर्भ को पास करना महत्वपूर्ण है, जिसे निष्पादित नहीं किया गया है। इसलिए

function foo(bla) {
  console.log(bla)
}

तो ज्यादातर समय (लेकिन हमेशा नहीं) आप पास fooनहीं होंगेfoo()

उम्मीद है कि कुछ समझ में आएगा। जब आप इस तरह की चीजों का सामना करते हैं जो भ्रामक लगती हैं - तो मैं कम से कम इसकी समझ हासिल करने के लिए प्रलेखन को पूरी तरह से पढ़ने की सलाह देता हूं। यह आपको बहुत बेहतर डेवलपर बना देगा।


18

ES2017 का उपयोग करते हुए आपके पास फ़ंक्शन घोषणा के रूप में यह होना चाहिए

async function foo() {
    var response = await $.ajax({url: '...'})
    return response;
}

और इसे इस तरह से अंजाम दे रहा है।

(async function() {
    try {
        var result = await foo()
        console.log(result)
    } catch (e) {}
})()

या वादा सिंटैक्स

foo().then(response => {
    console.log(response)

}).catch(error => {
    console.log(error)

})

पुन: प्रयोज्य करने के लिए वह दूसरा कार्य कर सकता है ??
झूम दमि

यदि आप ऑनकोल्स, लॉग को कहते हैं तो आप परिणामों का उपयोग कैसे करते हैं? क्या सब कुछ सिर्फ उस बिंदु पर कंसोल में नहीं जाता है?
केन इनग्राम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.