ट्रैक
/
JavaScript
JavaScript
/
अभ्यास
/
अनुवाद सेवा
अनुवाद सेवा

अनुवाद सेवा

सीखने का अभ्यास

परिचय

Promise ऑब्जेक्ट किसी एसिंक्रोनस काम के अंततः पूरा होने (या विफल होने) और उससे मिलने वाली वैल्यू को दर्शाता है।

Note

यह विषय बहुत लोगों के लिए कठिन है, खास तौर पर उनके लिए जो ऐसी भाषा में प्रोग्रामिंग जानते हैं जो पूरी तरह सिंक्रोनस है। अगर आपको यह बहुत भारी लग रहा है, या आप कॉन्करेंसी और पैरेललिज़्म के बारे में और जानना चाहते हैं, तो शानदार टॉक "Concurrency is not parallelism" को देखिए (go.dev के ज़रिए) या सीधे vimeo पर देखिए, और उसकी स्लाइड्स पढ़िए।

प्रॉमिस का जीवनचक्र

एक Promise की तीन स्थितियाँ होती हैं:

  1. pending
  2. fulfilled
  3. rejected

बनाते समय प्रॉमिस pending स्थिति में होता है। भविष्य में किसी समय वह resolve या reject हो सकता है। एक बार कोई प्रॉमिस resolve या reject हो जाए, तो वह दोबारा कभी resolve या reject नहीं हो सकता, और न ही उसकी स्थिति बदल सकती है।

दूसरे शब्दों में:

  1. pending स्थिति में एक प्रॉमिस:
    • fulfilled या rejected, इन दोनों में से किसी भी स्थिति में जा सकता है।
  2. fulfilled स्थिति में एक प्रॉमिस:
    • किसी भी अन्य स्थिति में नहीं जा सकता।
    • उसके पास एक वैल्यू होनी चाहिए, जो बदलनी नहीं चाहिए।
  3. rejected स्थिति में एक प्रॉमिस:
    • किसी भी अन्य स्थिति में नहीं जा सकता।
    • उसके पास एक कारण होना चाहिए, जो बदलना नहीं चाहिए।

प्रॉमिस को resolve करना

किसी प्रॉमिस को कई तरीकों से 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 करना

किसी प्रॉमिस को कई तरीकों से 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 करना चाहते हैं।

प्रॉमिस को chain करना

किसी प्रॉमिस के resolve या reject होने पर उसे भविष्य की किसी क्रिया के साथ जारी रखा जा सकता है।

  • promise के resolve होने पर promise.then() कॉल किया जाता है
  • promise के reject होने पर promise.catch() कॉल किया जाता है
  • promise के resolve या reject, किसी भी तरह पूरा होने पर promise.finally() कॉल किया जाता है

then

हर प्रॉमिस "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);
  • लगभग आधे मामलों में यह करीब 1000 ms के बाद "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 हो सकता है।

catch

कभी-कभी आप एरर पकड़ना चाहते हैं और सिर्फ तभी आगे बढ़ना चाहते हैं जब मूल प्रॉमिस 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" होगी;

finally

कभी-कभी आप किसी प्रॉमिस के 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 completed
  • जिन मामलों में randomNumber का मान 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 कर सकता है।

Note

अगर प्रॉमिस को चेन करना या उनका आम इस्तेमाल समझ नहीं आ रहा हो, तो 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.

⚠ चेतावनी! ⚠

Caution

API अपना कमाल यह करती है कि जब कोई request आती है, तो वह अलग-अलग अनुवादकों को तुरंत पहुँचा देती है। यह बहुत महँगा काम है, इसलिए जब कोई अनुवाद उपलब्ध हो, तब इसे कॉल नहीं करना चाहिए। बदकिस्मती से, हर कोई नियमावली नहीं पढ़ता, इसलिए नियम तोड़ने वालों को बाहर निकालने की एक व्यवस्था भी है।

अगर किसी उपलब्ध text के लिए api.request कॉल की जाती है, तो API इस कॉल के लिए एक AbusiveClientError देती है, और उसके बाद हर कॉल के लिए भी। अगर किसी चीज़ का अनुवाद पहले ही हो चुका है, तो आप उसका अनुवाद कभी अनुरोध न करें, यह सुनिश्चित कीजिए।

1. अनुवाद लाइए, गुणवत्ता को नज़रअंदाज़ करते हुए

मुफ्त सेवा सिर्फ वे अनुवाद देती है जो अभी API storage में मौजूद हैं।

एक मेथड free(text) बनाइए जो मुफ्त सदस्यों को वे अनुवाद देता है जो पहले से API storage में मौजूद हैं। गुणवत्ता को नज़रअंदाज़ कीजिए और API की ओर से आने वाली हर एरर आगे बढ़ा दीजिए।

  • अगर अनुवाद निकाला जा सकता है, तो उसे लौटाता है, चाहे उसकी गुणवत्ता कुछ भी हो
  • अनुवाद API की किसी भी एरर को आगे बढ़ा देता है
  • api.fetch मेथड इस्तेमाल करता है (api.fetch एक promise लौटाता है)
service.free('jIyaj');
// => Promise<...> resolves "I understand."

service.free("jIyajbe'");
// => Promise<...> rejects Error("Not yet translated")

2. अनुवादों का बैच लाइए, सब एक साथ वरना कुछ भी नहीं

मुफ्त सदस्यों के लिए एक मेथड 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()

3. अनुवाद का अनुरोध कीजिए, ज़्यादा से ज़्यादा 2 बार दोबारा कोशिश करते हुए

प्रीमियम उपयोगकर्ता के लिए एक मेथड 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

4. अनुवाद लाइए, गुणवत्ता जाँचिए, या उसका अनुरोध कीजिए

अनुवाद लाने के लिए प्रीमियम उपयोगकर्ता का एक मेथड 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?"

ध्यान दें

Note

'arlogh Qoylu'pu'? का सही अनुवाद How many times has it been heard? है।

GitHub के ज़रिए संपादित करें यह लिंक एक नई विंडो या टैब में खुलता है
JavaScript Exercism

अनुवाद सेवा शुरू करने के लिए तैयार हैं?

Exercism पर साइन अप कीजिए और JavaScript को 37 कॉन्सेप्ट159 अभ्यास तथा असली इंसानों से मिलने वाली मेंटरिंग के साथ सीखिए और उसमें महारत हासिल कीजिए, वह भी बिल्कुल मुफ्त।