यह आपके प्रश्न का पूर्ण उत्तर देने वाला नहीं है, लेकिन उम्मीद है कि इससे आपको और अन्य लोगों को उस समय मदद मिलेगी जब आप दस्तावेज़ को पढ़ने की कोशिश करेंगे $q
सेवा । इसे समझने में मुझे थोड़ा समय लगा।
आइए एक पल के लिए AngularJS को सेट करें और बस Facebook API कॉल पर विचार करें। जब Facebook से प्रतिक्रिया मिलती है, तो कॉल करने के लिए दोनों API कॉल एक कॉलबैक तंत्र का उपयोग करते हैं :
facebook.FB.api('/' + item, function (result) {
if (result.error) {
// handle error
} else {
// handle success
}
});
// program continues while request is pending
...
यह जावास्क्रिप्ट और अन्य भाषाओं में अतुल्यकालिक संचालन से निपटने के लिए एक मानक पैटर्न है।
इस पैटर्न के साथ एक बड़ी समस्या तब उत्पन्न होती है जब आपको अतुल्यकालिक संचालन के अनुक्रम को करने की आवश्यकता होती है, जहां प्रत्येक क्रमिक ऑपरेशन पिछले ऑपरेशन के परिणाम पर निर्भर करता है। यह कोड क्या कर रहा है:
FB.login(function(response) {
if (response.authResponse) {
FB.api('/me', success);
} else {
fail('User cancelled login or did not fully authorize.');
}
});
पहले यह लॉग इन करने की कोशिश करता है, और उसके बाद ही यह सत्यापित करने के बाद कि लॉगिन सफल था, यह ग्राफ एपीआई के लिए अनुरोध करता है।
यहां तक कि इस मामले में, जो केवल दो ऑपरेशनों का एक साथ पीछा कर रहा है, चीजें गड़बड़ होने लगती हैं। विधि askFacebookForAuthentication
विफलता और सफलता के लिए एक कॉलबैक स्वीकार करती है, लेकिन क्या होता है जब FB.login
सफल होता है लेकिन FB.api
असफल होता है? यह विधि विधि success
के परिणाम की परवाह किए बिना हमेशा कॉलबैक को आमंत्रित करती है FB.api
।
अब कल्पना करें कि आप तीन या अधिक अतुल्यकालिक ऑपरेशनों के एक मजबूत अनुक्रम को कोड करने की कोशिश कर रहे हैं, एक तरह से जो प्रत्येक चरण में त्रुटियों को ठीक से संभालता है और कुछ हफ्तों के बाद किसी और के लिए या आपके लिए भी उपयुक्त होगा। संभव है, लेकिन यह बहुत आसान है बस उन कॉलबैक को नेस्ट करना और रास्ते में त्रुटियों का ट्रैक खोना।
अब, चलिए एक पल के लिए Facebook API सेट करते हैं और $q
सेवा द्वारा कार्यान्वित की गई Angular Promises API पर विचार करते हैं । इस सेवा द्वारा लागू किया गया पैटर्न अतुल्यकालिक प्रोग्रामिंग को सरल कथनों की एक लीनियर श्रृंखला से मिलता-जुलता कुछ करने की कोशिश है, जिस तरह से किसी भी कदम पर एक त्रुटि को 'फेंक' करने और अंत में इसे संभालने की क्षमता, शब्दार्थ के समान परिचित try/catch
ब्लॉक।
इस आकस्मिक उदाहरण पर विचार करें। मान लें कि हमारे दो कार्य हैं, जहां दूसरा फ़ंक्शन पहले वाले परिणाम का उपभोग करता है:
var firstFn = function(param) {
// do something with param
return 'firstResult';
};
var secondFn = function(param) {
// do something with param
return 'secondResult';
};
secondFn(firstFn());
अब कल्पना करें कि FirstFn और secondFn दोनों को पूरा होने में लंबा समय लगता है, इसलिए हम इस क्रम को अतुल्यकालिक रूप से संसाधित करना चाहते हैं। पहले हम एक नई deferred
वस्तु बनाते हैं , जो संचालन की श्रृंखला का प्रतिनिधित्व करती है:
var deferred = $q.defer();
var promise = deferred.promise;
promise
संपत्ति श्रृंखला का अंतिम परिणाम प्रतिनिधित्व करता है। यदि आप इसे बनाने के तुरंत बाद एक वादा करते हैं, तो आप देखेंगे कि यह सिर्फ एक खाली वस्तु है ({}
) है। अभी तक देखने के लिए कुछ भी नहीं, सही साथ ले जाएँ।
अब तक हमारा वादा केवल श्रृंखला में शुरुआती बिंदु का प्रतिनिधित्व करता है। अब अपने दो ऑपरेशन जोड़ते हैं:
promise = promise.then(firstFn).then(secondFn);
then
विधि श्रृंखला के लिए एक कदम कहते हैं और फिर एक नई वादा विस्तारित श्रृंखला का अंतिम परिणाम का प्रतिनिधित्व करने देता है। आप जितने चाहें उतने चरण जोड़ सकते हैं।
अब तक, हमने अपने कार्यों की श्रृंखला स्थापित की है, लेकिन वास्तव में कुछ भी नहीं हुआ है। आपको कॉलिंग द्वारा शुरू की गई चीजें मिलती हैं deferred.resolve
, जो उस प्रारंभिक मूल्य को निर्दिष्ट करता है जिसे आप श्रृंखला के पहले वास्तविक चरण में पास करना चाहते हैं:
deferred.resolve('initial value');
और फिर ... फिर भी कुछ नहीं होता। यह सुनिश्चित करने के लिए कि मॉडल परिवर्तन ठीक से देखे गए हैं, जब तक अगली बार नहीं $apply
बुलाया जाता है , तब तक कोणीय वास्तव में श्रृंखला में पहला कदम नहीं कहता है:
deferred.resolve('initial value');
$rootScope.$apply();
// or
$rootScope.$apply(function() {
deferred.resolve('initial value');
});
तो त्रुटि से निपटने के बारे में क्या? अब तक हमने श्रृंखला में प्रत्येक चरण में केवल एक सफल हैंडलर निर्दिष्ट किया है। then
एक वैकल्पिक द्वितीय तर्क के रूप में एक त्रुटि हैंडलर को भी स्वीकार करता है। इस बार एक और वादा श्रृंखला का उदाहरण है, इस बार त्रुटि से निपटने के लिए:
var firstFn = function(param) {
// do something with param
if (param == 'bad value') {
return $q.reject('invalid value');
} else {
return 'firstResult';
}
};
var secondFn = function(param) {
// do something with param
if (param == 'bad value') {
return $q.reject('invalid value');
} else {
return 'secondResult';
}
};
var thirdFn = function(param) {
// do something with param
return 'thirdResult';
};
var errorFn = function(message) {
// handle error
};
var deferred = $q.defer();
var promise = deferred.promise.then(firstFn).then(secondFn).then(thirdFn, errorFn);
जैसा कि आप इस उदाहरण में देख सकते हैं, श्रृंखला के प्रत्येक हैंडलर के पास अगली सफलता के बजाय अगली त्रुटि हैंडलर पर ट्रैफ़िक को मोड़ने का अवसर है हैंडलर के । ज्यादातर मामलों में आपके पास श्रृंखला के अंत में एक एकल त्रुटि हैंडलर हो सकता है, लेकिन आपके पास मध्यवर्ती त्रुटि हैंडलर भी हो सकते हैं जो पुनर्प्राप्ति का प्रयास करते हैं।
अपने उदाहरणों (और आपके प्रश्नों) पर जल्दी लौटने के लिए, मैं सिर्फ इतना कहूंगा कि वे दो अलग-अलग तरीकों का प्रतिनिधित्व करते हैं जो फेसबुक के कॉलबैक-उन्मुख एपीआई को एंगुलर के मॉडल परिवर्तनों को देखने के तरीके के अनुकूल बनाते हैं। पहला उदाहरण एक वादे में एपीआई कॉल को लपेटता है, जिसे एक दायरे में जोड़ा जा सकता है और इसे एंगुलर के टेम्प्लेटिंग सिस्टम द्वारा समझा जाता है। दूसरा सीधे कॉलबैक परिणाम को स्कोप पर सेट करने का अधिक जानवर-बल दृष्टिकोण लेता है, और फिर $scope.$digest()
बाह्य स्रोत से परिवर्तन के बारे में कोणीय को अवगत कराने के लिए बुला रहा है।
दो उदाहरण सीधे तुलनीय नहीं हैं, क्योंकि पहला लॉगिन चरण याद कर रहा है। हालांकि, यह आम तौर पर अलग-अलग सेवाओं में इस तरह बाहरी एपीआई के साथ बातचीत को एनकैप्सुलेट करने के लिए वांछनीय है, और परिणामों को वादे के रूप में नियंत्रकों को वितरित करता है। इस तरह आप अपने नियंत्रकों को बाहरी चिंताओं से अलग रख सकते हैं, और नकली सेवाओं के साथ उन्हें अधिक आसानी से परख सकते हैं।