Promise ऑब्जेक्ट किसी एसिंक्रोनस काम के अंततः पूरा होने (या विफल होने) और उससे मिलने वाली वैल्यू को दर्शाता है।
यह विषय बहुत लोगों के लिए कठिन है, खास तौर पर उनके लिए जो ऐसी भाषा में प्रोग्रामिंग जानते हैं जो पूरी तरह सिंक्रोनस है। अगर आपको यह बहुत भारी लग रहा है, या आप कॉन्करेंसी और पैरेललिज़्म के बारे में और जानना चाहते हैं, तो शानदार टॉक "Concurrency is not parallelism" को देखिए (go.dev के ज़रिए) या सीधे vimeo पर देखिए, और उसकी स्लाइड्स पढ़िए।
एक Promise की तीन स्थितियाँ होती हैं:
बनाते समय प्रॉमिस pending स्थिति में होता है। भविष्य में किसी समय वह resolve या reject हो सकता है। एक बार कोई प्रॉमिस resolve या reject हो जाए, तो वह दोबारा कभी resolve या reject नहीं हो सकता, और न ही उसकी स्थिति बदल सकती है।
दूसरे शब्दों में:
किसी प्रॉमिस को कई तरीकों से resolve किया जा सकता है:
// Creates a promise that is immediately resolved
Promise.resolve(value);
// Creates a promise that is immediately resolved
new Promise((resolve) => {
resolve(value);
});
// Chaining a promise leads to a resolved promise
somePromise.then(() => {
// ...
return value;
});
ऊपर के उदाहरणों में value कुछ भी हो सकती है, जिसमें एक एरर, undefined, null या कोई दूसरा प्रॉमिस भी शामिल है।
आम तौर पर आप ऐसी वैल्यू के साथ resolve करना चाहते हैं जो एरर न हो।
किसी प्रॉमिस को कई तरीकों से reject किया जा सकता है:
// Creates a promise that is immediately rejected
Promise.reject(reason)
// Creates a promise that is immediately rejected
new Promise((_, reject) {
reject(reason)
})
// Chaining a promise with an error leads to a rejected promise
somePromise.then(() => {
// ...
throw reason
})
ऊपर के उदाहरणों में reason कुछ भी हो सकता है, जिसमें एक एरर, undefined या null भी शामिल है।
आम तौर पर आप एक एरर के साथ reject करना चाहते हैं।
किसी प्रॉमिस के resolve या reject होने पर उसे भविष्य की किसी क्रिया के साथ जारी रखा जा सकता है।
promise के resolve होने पर promise.then() कॉल किया जाता हैpromise के reject होने पर promise.catch() कॉल किया जाता हैpromise के resolve या reject, किसी भी तरह पूरा होने पर promise.finally() कॉल किया जाता हैहर प्रॉमिस "thenable" होता है।
इसका मतलब है कि उस पर एक then फंक्शन उपलब्ध होता है, जो मूल प्रॉमिस के resolve होने पर चलाया जाता है।
promise.then(onResolved) देने पर कॉलबैक onResolved को वह वैल्यू मिलती है जिसके साथ मूल प्रॉमिस resolve हुआ था।
यह हमेशा एक नया प्रॉमिस लौटाता है, जो चेन में जुड़ जाता है।
then से कोई value लौटाने पर चेन में जुड़ा प्रॉमिस resolve हो जाता है।
then में कोई reason throw करने पर चेन में जुड़ा प्रॉमिस reject हो जाता है।
const promise1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('Success!');
}, 1000);
});
const promise2 = promise1.then(function (value) {
console.log(value);
// expected output: "Success!"
return true;
});
यह लगभग 1000 ms के बाद "Success!" लॉग करेगा।
promise1 की स्थिति resolved और वैल्यू "Success!" होगी।
promise2 की स्थिति resolved और वैल्यू true होगी।
एक दूसरा आर्गुमेंट भी उपलब्ध है, जो मूल प्रॉमिस के reject होने पर चलता है।
promise.then(onResolved, onRejected) देने पर कॉलबैक onResolved को वह वैल्यू मिलती है जिसके साथ मूल प्रॉमिस resolve हुआ था, या कॉलबैक onRejected को वह कारण मिलता है जिसके साथ प्रॉमिस reject हुआ था।
const promise1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('Success!');
}, 1000);
if (Math.random() < 0.5) {
reject('Nope!');
}
});
function log(value) {
console.log(value);
return true;
}
function shout(reason) {
console.error(reason.toUpperCase());
return false;
}
const promise2 = promise1.then(log, shout);
"Success!" लॉग करेगा।
promise1 की स्थिति resolved और वैल्यू "Success!" होगी।promise2 की स्थिति resolved और वैल्यू true होगी।"NOPE!" लॉग करेगा।
promise1 की स्थिति rejected और वैल्यू Nope! होगी।promise2 की स्थिति resolved और वैल्यू false होगी।यह समझना ज़रूरी है कि जीवनचक्र के नियमों की वजह से, जब कोई प्रॉमिस reject होता है, तो लगभग 1000ms बाद आने वाला resolve चुपचाप नज़रअंदाज़ कर दिया जाता है, क्योंकि एक बार reject या resolve हो जाने के बाद अंदरूनी स्थिति बदल नहीं सकती।
यह भी समझना ज़रूरी है कि किसी प्रॉमिस से कोई वैल्यू लौटाना उसे resolve कर देता है, और कोई वैल्यू throw करना उसे reject कर देता है।
जब promise1 resolve होता है और उसके साथ एक चेन में जुड़ा onResolved होता है: then(onResolved), तो वह आगे वाला एक नया प्रॉमिस होता है, जो resolve या reject हो सकता है।
जब promise1 reject होता है, लेकिन उसके साथ एक चेन में जुड़ा onRejected होता है: then(, onRejected), तो वह आगे वाला एक नया प्रॉमिस होता है, जो resolve या reject हो सकता है।
कभी-कभी आप एरर पकड़ना चाहते हैं और सिर्फ तभी आगे बढ़ना चाहते हैं जब मूल प्रॉमिस reject हो।
promise.catch(onCatch) देने पर कॉलबैक onCatch को वह कारण मिलता है जिसके साथ मूल प्रॉमिस reject हुआ था।
यह हमेशा एक नया प्रॉमिस लौटाता है, जो चेन में जुड़ जाता है।
catch से कोई value लौटाने पर चेन में जुड़ा प्रॉमिस resolve हो जाता है।
catch में कोई reason throw करने पर चेन में जुड़ा प्रॉमिस reject हो जाता है।
const promise1 = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve('Success!');
}, 1000);
if (Math.random() < 0.5) {
reject('Nope!');
}
});
function log(value) {
console.log(value);
return 'done';
}
function recover(reason) {
console.error(reason.toUpperCase());
return 42;
}
const promise2 = promise1.catch(recover).then(log);
लगभग आधे मामलों में यह करीब 1000 ms के बाद "Success!" लॉग करेगा।
बाकी आधे मामलों में यह तुरंत 42 लॉग करेगा।
promise1 resolve होता है, तो catch छोड़ दिया जाता है और यह then तक पहुँचकर वैल्यू लॉग करता है।
promise1 की स्थिति resolved और वैल्यू "Success!" होगी।promise2 की स्थिति resolved और वैल्यू "done" होगी;promise1 reject होता है, तो catch चलाया जाता है, जो एक वैल्यू लौटाता है, और इस तरह चेन अब resolved हो जाती है, फिर यह then तक पहुँचकर वैल्यू लॉग करता है।
promise1 की स्थिति rejected और वैल्यू "Nope!" होगी।promise2 की स्थिति resolved और वैल्यू "done" होगी;कभी-कभी आप किसी प्रॉमिस के settle होने के बाद कोड चलाना चाहते हैं, चाहे वह resolve हो या reject।
promise.finally(onSettled) देने पर कॉलबैक onSettled को कुछ नहीं मिलता।
यह हमेशा एक नया प्रॉमिस लौटाता है, जो चेन में जुड़ जाता है।
finally से कोई value लौटाने पर मूल प्रॉमिस की स्थिति और वैल्यू वैसी ही रहती हैं, और लौटाई गई value को अनदेखा कर दिया जाता है।
finally में कोई reason throw करने पर चेन में जुड़ा प्रॉमिस reject हो जाता है, और मूल प्रॉमिस की स्थिति, वैल्यू या कारण पर वही लिख देता है।
कई मेथड एक साथ:
const myPromise = new Promise(function (resolve, reject) {
const sampleData = [2, 4, 6, 8];
const randomNumber = Math.round(Math.random() * 5);
if (sampleData[randomNumber]) {
resolve(sampleData[randomNumber]);
} else {
reject('Sampling did not result in a sample');
}
});
const finalPromise = myPromise
.then(function (sampled) {
// If the random number was 0, 1, 2, or 3, this will be
// reached and the number 2, 4, 6, or 8 will be logged.
console.log(`Sampled data: ${sampled}`);
return 'yay';
})
.catch(function (reason) {
// If the random number was 4 or 5, this will be reached and
// reason will be "An error occurred". The entire chain will
// then reject with an Error with the reason as message.
throw new Error(reason);
})
.finally(function () {
// This will always log after either the sampled data is
// logged or the error is raised.
console.log('Promise completed');
});
randomNumber का मान 0-3 होता है:
myPromise वैल्यू 2, 4, 6, or 8 के साथ resolve होता हैfinalPromise वैल्यू 'yay' के साथ resolve होता हैSampled data: ...Promise completedrandomNumber का मान 4-5 होता है:
myPromise कारण 'Sampling did not result in a sample' के साथ reject होता हैfinalPromise कारण Error('Sampling did not result in a sample') के साथ reject होता हैPromise completed"uncaught rejected promise: Error('Sampling did not result in a sample')" लॉग देगाजैसा ऊपर दिखाया गया है, reject एक स्ट्रिंग के साथ भी काम करता है, और कोई प्रॉमिस एक Error के साथ भी reject कर सकता है।
अगर प्रॉमिस को चेन करना या उनका आम इस्तेमाल समझ नहीं आ रहा हो, तो MDN पर ट्यूटोरियल पढ़ना अच्छा स्रोत है।
इस अभ्यास में आप एक TranslationService बनाएँगे, जो मुफ्त सदस्यों को साधारण अनुवाद सेवाएँ देता है और प्रीमियम सदस्यों को गुणवत्ता की गारंटी के साथ उन्नत अनुवाद।
API
आपको अंतरिक्ष की एक अनुवाद API मिली है, जो किसी भी अनुवाद request को उचित समय में पूरा कर देती है।
आप इसका फायदा उठाना चाहते हैं।
अंतरिक्ष के अनुवादक बहुत मनमौजी हैं और दोहराव से चिढ़ते हैं, इसलिए वे API storage सैटेलाइट भी देते हैं, जहाँ से आप पुराने अनुवाद fetch कर सकते हैं, बिना उन्हें परेशान किए।
एक अनुवाद लाना
api.fetch(text) API storage से text का अनुवाद लाता है और एक promise लौटाता है जो दो वैल्यू देता है:
translation: असली अनुवादquality: एक संख्या के रूप में व्यक्त गुणवत्ताअगर API storage में अनुवाद नहीं मिलता, तो API एक NotAvailable एरर देती है।
अनुवाद api.request मेथड से जोड़े जा सकते हैं।
अगर 'text' का अनुवाद नहीं हो सकता, तो API एक Untranslatable एरर देती है।
api.fetch('jIyaj');
// => Promise({ resolved: 'I understand' })
एक अनुवाद का अनुरोध करना
कुछ अनुवादों का होना तय है, लेकिन वे अभी API storage में जोड़े नहीं गए हैं। यही NotAvailable (storage में नहीं है, पर अनुरोध किया जा सकता है) और Untranslatable (अनुवाद हो ही नहीं सकता) के बीच का फर्क है।
api.request(text, callback) अनुरोध करता है कि text का अनुवाद किया जाए और उसे API storage में जोड़ा जाए।
पूरा होने पर callback फंक्शन को कॉल किया जाता है।
callback को undefined दिया जाता है: इसका मतलब है कि अनुवाद सफल रहा और उसे api.fetch मेथड से पाया जा सकता है।callback को एक error दिया जाता है: इसका मतलब है कि कुछ गड़बड़ हुई।
अंतरिक्ष की API अस्थिर है, यानी यह अक्सर फेल होती है।
ऐसा हो तो दोबारा api.request करना ठीक है।api.request('majQa’', callback);
// => undefined
//
// later: the passed callback is called with undefined
// because it was successful.
⚠ चेतावनी! ⚠
API अपना कमाल यह करती है कि जब कोई request आती है, तो वह अलग-अलग अनुवादकों को तुरंत पहुँचा देती है।
यह बहुत महँगा काम है, इसलिए जब कोई अनुवाद उपलब्ध हो, तब इसे कॉल नहीं करना चाहिए।
बदकिस्मती से, हर कोई नियमावली नहीं पढ़ता, इसलिए नियम तोड़ने वालों को बाहर निकालने की एक व्यवस्था भी है।
अगर किसी उपलब्ध text के लिए api.request कॉल की जाती है, तो API इस कॉल के लिए एक AbusiveClientError देती है, और उसके बाद हर कॉल के लिए भी।
अगर किसी चीज़ का अनुवाद पहले ही हो चुका है, तो आप उसका अनुवाद कभी अनुरोध न करें, यह सुनिश्चित कीजिए।
मुफ्त सेवा सिर्फ वे अनुवाद देती है जो अभी API storage में मौजूद हैं।
एक मेथड free(text) बनाइए जो मुफ्त सदस्यों को वे अनुवाद देता है जो पहले से API storage में मौजूद हैं।
गुणवत्ता को नज़रअंदाज़ कीजिए और API की ओर से आने वाली हर एरर आगे बढ़ा दीजिए।
api.fetch मेथड इस्तेमाल करता है (api.fetch एक promise लौटाता है)service.free('jIyaj');
// => Promise<...> resolves "I understand."
service.free("jIyajbe'");
// => Promise<...> rejects Error("Not yet translated")
मुफ्त सदस्यों के लिए एक मेथड batch([text, text, ...]) बनाइए जो मुफ्त सेवा से टेक्स्ट के ऐरे का अनुवाद करता है और सारे अनुवाद लौटाता है, या एक ही एरर।
BatchIsEmpty एरर के साथ रिजेक्ट होता हैservice.batch(['jIyaj', "majQa'"]);
// => Promise<...> resolves ["I understand.", "Well done!"]
service.batch(['jIyaj', "jIyajbe'"]);
// => Promise<...> rejects new Error("Not yet translated")
service.batch([]);
// => Promise<...> rejects BatchIsEmpty()
प्रीमियम उपयोगकर्ता के लिए एक मेथड request(text) बनाइए, जो API storage में एक अनुवाद जोड़ने का अनुरोध करता है।
अगर कोई गड़बड़ी हो, तो अनुरोध को अपने आप दोबारा कोशिश करनी चाहिए।
एक ही अनुरोध के लिए यह 3 से ज़्यादा कॉल न करे (अंतरिक्ष के अनुवादकों को नाराज़ मत कीजिए!!!)।
api.request कोई एरर नहीं लौटाता, तो undefined के साथ रिज़ॉल्व कीजिएapi.request एरर लौटाता है, तो ज़्यादा से ज़्यादा दो बार दोबारा कोशिश कीजिएservice.request("jIyajbe'");
// => Promise<...> resolves (with nothing), can now be retrieved using the fetch API
अनुवाद लाने के लिए प्रीमियम उपयोगकर्ता का एक मेथड premium(text, quality) बनाइए।
अगर कोई अनुवाद NotAvailable है, तो उस अनुवाद का अनुरोध कीजिए और API storage में जुड़ने के बाद उसे ले लीजिए।
मेथड तभी अनुवाद लौटाए, जब वह एक तय quality सीमा पर खरा उतरता हो।
api.fetch रिज़ॉल्व होता है, तो रिज़ॉल्व करने से पहले गुणवत्ता जाँचिएapi.fetch रिजेक्ट होता है, तो इसके बजाय अनुवाद का अनुरोध कीजिएapi.request रिजेक्ट होता है, तो एरर आगे बढ़ा दीजिएservice.premium("jIyajbe'", 100);
// => Promise<...> resolves "I don't understand."
service.premium("'arlogh Qoylu'pu'?", 100);
// => Promise<...> rejects QualityThresholdNotMet()
service.premium("'arlogh Qoylu'pu'?", 40);
// => Promise<...> resolves "What time is it?"
ध्यान दें
'arlogh Qoylu'pu'? का सही अनुवाद How many times has it been heard? है।
Exercism पर साइन अप कीजिए और JavaScript को 37 कॉन्सेप्ट159 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।