javascript - मैं एक एसिंक्रोनस कॉल से प्रतिक्रिया कैसे वापस कर सकता हूं?




ajax asynchronous ecmascript-6 ecmascript-2017 (25)

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 नाम का उपयोग करके तीन):

सबसे साधारण:

this.response

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

e.target.response

उदाहरण:

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

या (उपर्युक्त एक बेहतर अज्ञात कार्य हमेशा एक समस्या है):

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

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

अब कुछ लोग शायद कहेंगे कि पहले से हीस्टेन्चेंज या यहां तक ​​कि XMLHttpRequest चर नाम का उपयोग करना बेहतर है। यह गलत है।

XMLHttpRequest उन्नत सुविधाओं को देखें

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

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

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);

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

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

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

यहां तक ​​कि यदि आप विधि के रूप में 'POSTAPAPAP' पास करते हैं तो यह कोई त्रुटि नहीं फेंक देगा।

भले ही आप 'fdggdgilfdghfldj' को फॉर्मडाटा के रूप में पास करते हैं, यह एक त्रुटि नहीं फेंक देगा।

पहले मामले में त्रुटि displayAjax() के अंदर है। इस Method not Allowed तहत this.statusText को Method not Allowed गई Method not Allowed रूप में।

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

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

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

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

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

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

पीएस: पहले परीक्षण के रूप में मैंने एक्स ('एक्स', डिस्प्लेएजेक्स) लिखा ..., और इसे पूरी तरह से प्रतिक्रिया मिली ... ??? इसलिए मैंने उस फ़ोल्डर को चेक किया जहां HTML स्थित है, और 'x.xml' नामक एक फ़ाइल थी। तो अगर आप अपनी फ़ाइल का विस्तार भूल जाते हैं तो XMLHttpRequest 2 इसे ढूंढ जाएगा । मैंने लोल किया

एक फ़ाइल तुल्यकालिक पढ़ें

ऐसा मत करो।

यदि आप थोड़ी देर के लिए ब्राउज़र को अवरुद्ध करना चाहते हैं तो एक अच्छी बड़ी 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');

गैर-असीमित तरीके से ऐसा करने का कोई और तरीका नहीं है। (हाँ, setTimeout पाश के साथ ... लेकिन गंभीरता से?)

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

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

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

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

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

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

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

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

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

मेरे पास एक फ़ंक्शन 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`.

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

आइए एक साधारण जावास्क्रिप्ट फ़ंक्शन से शुरू करें:

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

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

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

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

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

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

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

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

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

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 fetchUsers(){
   let users = [];
   getUsers()
   .then(_users => users = _users)
   .catch(err =>{
      throw err
   })
   return users;
 }

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

  async function fetchUsers(){
     try{
        let users = await getUsers()
        return users;
     }
     catch(err){
        throw err;
     }
  }

पेड़ को देखने से पहले जंगल को पहले देखते हैं।

यहां महान विवरण के साथ कई सूचनात्मक उत्तर हैं, मैं उनमें से किसी को दोहराना नहीं चाहूंगा। जावास्क्रिप्ट में प्रोग्रामिंग की कुंजी पहले निष्पादन का सही मानसिक मॉडल है।

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

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

लेकिन जब तक आप वास्तविक समस्या डोमेन से सहज नहीं हैं तब तक आपको किसी समस्या को हल करने के लिए किसी भी सामरिक उपकरण का उपयोग नहीं करना चाहिए। यह निर्भर करने के लिए कि इन्हें चलाने की आवश्यकता है, इन निर्भरताओं का नक्शा बनाएं। इन सभी कॉलबैक के लिए एक विज्ञापन-प्रसार दृष्टिकोण का प्रयास करना सिर्फ आपकी सेवा करने वाला नहीं है।


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

अर्थात्:

function handleData( responseData ) {

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

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

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


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)

})

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

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 को वापस कर रहा है (यह लौटने पर हल हो जाता है )। तो आप $ http.get अनुरोध पूरा होने तक प्रतीक्षा करें और फिर console.log (res.joke) निष्पादित किया जाता है (एक सामान्य असीमित प्रवाह के रूप में)।res.data.value

यह plnkr है:

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


आप पर कोड फेंकने की बजाय, 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 का उपयोग करके कॉल करते हैं या किसी भी एसिंक्रोनस कोड (उदाहरण के लिए सेटटाइमआउट) चलाते हैं, तो जावास्क्रिप्ट आगे बढ़ने से पहले प्रतिक्रिया पर निर्भर होता है।

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

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

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

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

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

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

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


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

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

उदाहरण:

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

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

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

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

समानांतर

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

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
        }
    });
});

उदाहरण:

var theArray = [1, 2, 3];
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
        }
    });
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

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

ध्यान दें कि परिणाम को उसी स्थिति में सहेजने के लिए हम किस प्रकार indexसे प्रवेश करते हैं, जैसे परिणाम संबंधित होते हैं, भले ही परिणाम ऑर्डर से बाहर आते हैं (क्योंकि एसिंक कॉल जरूरी क्रम में पूरा नहीं होते हैं)।forEachresults

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

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);
});

उदाहरण:

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([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

या यहां एक संस्करण 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मिलने पर वादे को अस्वीकार करने के लिए उपयोग किया जाएगा।)

उदाहरण:

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([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

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

अगर doSomethingAsyncआपको then() , तो आप इसका उपयोग कर सकते हैं Promise.all:

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

उदाहरण:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry, function(result) {
            results.push(result);
        });
    }));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

ध्यान दें कि 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;, लेकिन निम्नलिखित में से कुछ उदाहरणों में हमारे पास इंडेक्स नहीं है उपयोग करने के लिए।)

उदाहरण:

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([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(या फिर, इसके लिए एक रैपर बनाएं doSomethingAsyncजिससे आपको वादा किया जा सके और नीचे ...)

यदि doSomethingAsyncआप एक वादा देता है, अगर आप (जैसे एक transpiler साथ शायद ES2017 + सिंटैक्स का उपयोग कर सकते हैं कोलाहल ), आप एक का उपयोग कर सकते async/await के साथ 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);
});

उदाहरण:

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

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

यदि आप 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);
});

उदाहरण:

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([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

... जो 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);
});

उदाहरण:

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

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}


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

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

आपका कोड इस तरह के साथ कुछ होना चाहिए:

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 का उपयोग करने वाले लोगों के लिए एक उत्तर लिखने का एक अच्छा काम किया। मेरे पास देशी एक्सएचआर का जवाब है। यह उत्तर फ्रंटेंड या बैकएंड पर या तो वादे के सामान्य उपयोग के लिए है।

मुख्य मुद्दा

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

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

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

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

    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, सर्वर कॉल, आईओ, टाइमर) अनुरोध से पहले आप मान को वापस कर रहे हैं कि आपके कोड को यह बताने का मौका मिला कि वह मूल्य क्या है।

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

वादे पर त्वरित पुनरावृत्ति

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

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

एक वादा केवल राज्यों को बदल सकता है जिसके बाद यह हमेशा के लिए एक ही राज्य में हमेशा रहेगा। आप then हैंडलर को अपने मूल्य निकालने और त्रुटियों को संभालने का वादा करने के लिए संलग्न कर सकते हैं। then हैंडलर कॉल की chaining की अनुमति देते हैं। वादे उन एपीआई का उपयोग करके बनाए जाते हैं जो उन्हें वापस कर देते हैं । उदाहरण के लिए, अधिक आधुनिक 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

इसे लागू करना

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

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 generators पेश करता generators जो कि फ़ंक्शन हैं जो बीच में वापस आ सकते हैं और फिर उस बिंदु को फिर से शुरू कर सकते हैं। यह आमतौर पर अनुक्रमों के लिए उपयोगी है, उदाहरण के लिए:

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यहां ब्लूबर्ड का उपयोग करूँगा , लेकिन अन्य रैपर जैसे 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)

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

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
}

यह अभी भी वही वचन देता है :)


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

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

समस्या

Ajax में asynchronous के लिए खड़ा है। इसका मतलब है कि अनुरोध भेजना (या प्रतिक्रिया प्राप्त करना) सामान्य निष्पादन प्रवाह से बाहर निकाला जाता है। आपके उदाहरण में, $.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(); बाद आ रहा है var item = findItem(); फ़ंक्शन परिणाम लौटने तक प्रतीक्षा करना होगा

अतुल्यकालिक

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

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

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

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

समाधान की)

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

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

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

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

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

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

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

ES2017 +: async/await साथ वादा async/await

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

async/await वादे के शीर्ष पर बनाता है: एक async समारोह हमेशा एक वादा देता है। एक वादा "अनचाहे" का await और या तो वादा को अस्वीकार कर दिया गया था या तो वादा को अस्वीकार कर दिया गया था या तो वादा को खारिज कर दिया गया था।

महत्वपूर्ण: आप केवल async फ़ंक्शन के अंदर await उपयोग कर सकते हैं। इसका मतलब है कि बहुत ऊपर के स्तर पर, आपको अभी भी वादे के साथ सीधे काम करना है।

आप async/await बारे में अधिक पढ़ सकते हैं और एमडीएन पर 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;
  }
}

// Async functions always return a promise
getAllBooks()
  .then(function(books) {
    console.log(books);
  });

वर्तमान browser और node संस्करण async/await समर्थन करते हैं। आप regenerator (या उपकरण जो पुनर्विक्रेता का उपयोग करते हैं, जैसे Babel ) की मदद से ईएस 5 में अपना कोड बदलकर पुराने वातावरण का समर्थन कर सकते हैं।

कार्यों को कॉलबैक स्वीकार करने दें

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

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

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

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

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

ES2015 +: then() साथ वादे then()

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

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

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

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

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
  });

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

वादे के बारे में अधिक जानकारी: एचटीएमएल 5 चट्टानों - जावास्क्रिप्ट वादा करता है

साइड नोट: jQuery की स्थगित वस्तुओं

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

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 अजाक्स डिफरर्ड ऑब्जेक्ट देता है, प्रतिक्रिया नहीं सर्वर। इसका मतलब है कि 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 कॉलबैक का उपयोग कर सकते हैं या responseText उपयोग कर सकते हैं jqXHR ऑब्जेक्ट की टेक्स्ट गुण:

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

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

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


Angular1

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

Here यह कहता है,

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

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

नीचे उल्लिखित docs में पाया गया उदाहरण ।

  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निम्नलिखित उदाहरण को देखो, लेकिन साथ अपने recommended का उपयोग करने के 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));
}

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

इसके अतिरिक्त यहां वादे का spec परिभाषित किया गया है।


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

यहां इसका एक उदाहरण दिया गया है:

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एसिंक्रोनस ऑपरेशन के दौरान मूल्य को स्टोर करने के लिए ऑब्जेक्ट का उपयोग कर रहा हूं । यह परिणाम असीमित काम के बाद भी उपलब्ध हो सकता है।

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


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


सफलता के 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();

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

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

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

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-style तीर फ़ंक्शंस का उपयोग करना अक्सर सहायक होता है:

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);
}

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

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
}

आप here परियोजना की जांच कर सकते here


यदि आप अपने कोड में 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'

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

( नोट, नए fetch API, कोणीय या वादे का उपयोग करने वालों के लिए मैंने नीचे एक और जवाब जोड़ा है )

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

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

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

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

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

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

(Fiddle)

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 कहा जाता है। असल में, हम पास होने पर निष्पादित करने के लिए एक क्रिया को प्राप्त कर रहे हैं, हम एक घटना पूर्ण होने पर हमारे कोड को कैसे प्रतिक्रिया दे रहे हैं (जैसे हमारे AJAX कॉल, या इस मामले में टाइमआउट)।

उपयोग होगा:

getFive(onComplete);

स्क्रीन पर "5" को सतर्क करना चाहिए। (Fiddle)

संभव समाधान

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

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

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

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

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 पूर्ण होने पर प्रतिक्रिया कैसे करें।

इसलिए:

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();
}

(fiddle)

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

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


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

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

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

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

(async function(){

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

})()

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


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

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

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


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

function* myGenerator() {
    const callback = yield;
    let [response] = yield $.ajax("https://.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);

सबसे सरल समाधान एक जावास्क्रिप्ट फ़ंक्शन बना रहा है और इसे अजाक्स 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);    
}); 

मैंने जो उदाहरण दिया है, वह दिखाता है कि कैसे करें

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

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

प्रसंग। उदाहरण क्वेरी स्ट्रिंग्स के दिए गए सेट के लिए ऑब्जेक्ट्स को खोजने के लिए स्पॉटिफा वेब एपीआई एंडपॉइंट से पूछताछ कर रहा है playlist:

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

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

फिर आप एक नेस्टेड वादा संरचना देख सकते हैं, जो आपको एकाधिक और पूरी तरह से एसिंक्रोनस नेस्टेड 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" />

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


एक और समाधान अनुक्रमिक निष्पादक nsynjs के माध्यम से कोड निष्पादित 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. nnsynjs के माध्यम से तुल्यकालिक तरीके से कार्य चलाएं:

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

Nsynjs सभी ऑपरेटरों और अभिव्यक्तियों का मूल्यांकन चरण-दर-चरण का मूल्यांकन करेगा, अगर कुछ धीमे फ़ंक्शन का परिणाम तैयार नहीं होता है तो निष्पादन रोकना।

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


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

  1. then()
  2. Q - जावास्क्रिप्ट के लिए एक वादा पुस्तकालय
  3. ए + Promises.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();


उदाहरण

var a  = "Test String";

if(a.search("ring")!=-1){
     //exist 
} else {
     //not found 
}




javascript ajax asynchronous ecmascript-6 ecmascript-2017