→ विभिन्न उदाहरणों के साथ 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 हमेशा अतुल्यकालिक है (इस विकल्प पर विचार नहीं करने का एक और कारण)।