JavaScript Promises: परिचय

प्रॉमिस, डिफ़र्ड और एसिंक्रोनस कंप्यूटेशन को आसान बनाता है. प्रॉमिस ऐसी कार्रवाई को दिखाता है जो अभी तक पूरी नहीं हुई है.

डेवलपर, YouTube के इतिहास के एक अहम पल के लिए खुद को तैयार करें वेब डेवलपमेंट के बारे में ज़्यादा जानें.

[ड्रमरोल की शुरुआत]

JavaScript में वादे पूरे हो गए हैं!

[पटाखे फोड़ते हैं, ऊपर से चमचमाते काग़ज़ की बारिश होती है, भीड़ का सामना करना पड़ता है]

इस समय आप इनमें से किसी एक कैटगरी में आते हैं:

  • लोग आपके आस-पास खुश हो रहे हैं, लेकिन पता नहीं है कि क्या अफ़वाह है के बारे में. शायद आपको पक्के तौर पर यह भी पता न हो कि किस तरह का "वादा" दिया गया है है. आपने कंधे उचकाया, लेकिन चमचमाते हुए पेपर का वज़न कम हो रहा है. अगर ऐसा है, तो इसकी चिंता नहीं करनी, लेकिन यह काम करने में मुझे कई उम्र लग गई कि मुझे इसकी परवाह क्यों करनी चाहिए चीज़ें. आप शायद शुरुआत से इसे शुरू करना चाहें.
  • आपने हवा में एक छलांग लगाई है! समय के बारे में सही जानकारी? आपने 'प्रॉमिस' में मौजूद इन सुविधाओं को पहले भी इस्तेमाल किया है लेकिन आपको इस बात से परेशानी है कि लागू होने वाले हर एपीआई के लिए थोड़ा अलग एपीआई होता है. JavaScript के आधिकारिक वर्शन का एपीआई क्या है? आप शायद शुरू करना चाहें शब्दावली का इस्तेमाल करें.
  • आपको यह पहले से पता था और आप उन लोगों का मज़ाक़ उड़ाते हैं जो उछलते हैं और उनके लिए समाचार का काम कर सकते हैं. कुछ समय निकालकर खुद को बेहतर बनाएं, इसके बाद, सीधे एपीआई के रेफ़रंस पर जाएं.

ब्राउज़र सहायता और पॉलीफ़िल

ब्राउज़र सहायता

  • Chrome: 32. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
  • एज: 12. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
  • Firefox: 29. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
  • सफ़ारी: 8. अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है

सोर्स

ऐसे ब्राउज़र को लाने के लिए जिनका पूरा वादा नहीं किया गया हो या अन्य ब्राउज़र और Node.js के लिए प्रॉमिस जोड़ें पॉलीफ़िल (2k gzip).

ये सब किस बारे में है?

JavaScript एकल थ्रेड वाला है, जिसका मतलब है कि स्क्रिप्ट के दो बिट एक ही समय पर; उन्हें एक के बाद एक दौड़ना पड़ता है. ब्राउज़र में, JavaScript ऐसे कई सारे कॉन्टेंट के साथ थ्रेड शेयर करता है जो ब्राउज़र से अलग हैं ब्राउज़र खोलें. हालांकि, आम तौर पर JavaScript उसी सूची में होती है जिसमें पेंटिंग होती हैं और उसे अपडेट किया जाता है और उपयोगकर्ता की कार्रवाइयों को मैनेज करना. जैसे, टेक्स्ट हाइलाइट करना और इंटरैक्ट करना फ़ॉर्म कंट्रोल के साथ). इनमें से किसी एक वजह से, अन्य वजहों से देरी हो रही है.

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

इसका पता लगाने के लिए, आपने इवेंट और कॉलबैक का इस्तेमाल किया होगा. इवेंट की जानकारी यहां दी गई है:

var img1 = document.querySelector('.img-1');

img1.addEventListener('load', function() {
  // woo yey image loaded
});

img1.addEventListener('error', function() {
  // argh everything's broken
});

यह कोई घबराहट नहीं है. हमें इमेज मिलती है, कुछ लिसनर जोड़ें, फिर JavaScript तब तक एक्ज़ीक्यूट नहीं करता, जब तक उन लिसनर को कॉल नहीं किया जाता.

माफ़ करें, ऊपर दिए गए उदाहरण में शायद ये इवेंट हुए हों इससे पहले कि हमने उनकी बात सुनना शुरू किया, इसलिए हमें इस पर काम करने की ज़रूरत है. "पूरा हो गया" इमेज की प्रॉपर्टी:

var img1 = document.querySelector('.img-1');

function loaded() {
  // woo yey image loaded
}

if (img1.complete) {
  loaded();
}
else {
  img1.addEventListener('load', loaded);
}

img1.addEventListener('error', function() {
  // argh everything's broken
});

इसमें ऐसी इमेज शामिल नहीं की जा सकतीं जिनकी वजह से, हमें सुनने का मौका मिलने से पहले गड़बड़ी हुई थी them; माफ़ करें, डीओएम से हमें ऐसा करने का कोई तरीका नहीं मिलता. साथ ही, यह एक इमेज लोड हो रही है. अगर हमें पता करना है कि सेट अप करने के लिए में से इमेज लोड हो गई हैं.

इवेंट हमेशा सही नहीं होते

एक ही बार में कई बार होने वाले इवेंट के लिए इवेंट सबसे बेहतर होते हैं ऑब्जेक्ट—keyup, touchstart वगैरह. उन इवेंट के साथ, जो आपकी आपके प्रशंसकों को अटैच करने से पहले क्या हुआ. हालाँकि, जब बात एक साथ काम नहीं करते हैं, तो आपको ऐसा कुछ चाहिए होगा:

img1.callThisIfLoadedOrWhenLoaded(function() {
  // loaded
}).orIfFailedCallThis(function() {
  // failed
});

// and…
whenAllTheseHaveLoaded([img1, img2]).callThis(function() {
  // all loaded
}).orIfSomeFailedCallThis(function() {
  // one or more failed
});

आम तौर पर, वादे का यही काम होता है, लेकिन इसे बेहतर नाम दिया जाता है. अगर एचटीएमएल इमेज एलिमेंट में "तैयार" विधि से प्रॉमिस मिला, तो हम ऐसा कर सकते हैं:

img1.ready()
.then(function() {
  // loaded
}, function() {
  // failed
});

// and…
Promise.all([img1.ready(), img2.ready()])
.then(function() {
  // all loaded
}, function() {
  // one or more failed
});

मूल रूप से, वादे इवेंट लिसनर जैसे कुछ होते हैं, इन्हें छोड़कर:

  • कोई वादा सिर्फ़ एक बार पूरा या फ़ेल हो सकता है. यह दो बार सफल या असफल नहीं हो सकता, न तो सफलता से असफलता पर स्विच कर सकता है और न ही इसका उलटा.
  • अगर कोई प्रॉमिस सफल या पूरा नहीं होता है और बाद में उसे पूरा होने या फ़ेल होने की जानकारी जोड़ी जाती है कॉलबैक न हो, तो सही कॉलबैक को कॉल किया जाएगा, भले ही इवेंट ने लिया हो पहले की जगह.

यह बहुत काम का है, क्योंकि सिस्टम के साथ काम नहीं कर पाता, किसी चीज़ के उपलब्ध होने का सटीक समय जानने में और ज़्यादा दिलचस्पी है नतीजे पर प्रतिक्रिया देने में.

प्रोमिस शब्दावली

डॉमिनिक डेनिकोला प्रूफ़ का पहला ड्राफ़्ट पढ़ें और मुझे "F" ग्रेड दिया है इस्तेमाल करें. उसने मुझे कैद में रखा, ज़बरदस्ती कॉपी किया राज्य और कहानी 100 बार इसने अपने माता-पिता को चिंतित चिट्ठी लिखी. इसके बावजूद, मैं अब भी बहुत सारी शब्दावली का इस्तेमाल किया है, लेकिन ये रही कुछ बुनियादी बातें:

प्रॉमिस:

  • fullfill - प्रॉमिस से जुड़ी कार्रवाई पूरी हो गई
  • rejected - प्रॉमिस से जुड़ी कार्रवाई पूरी नहीं हो सकी
  • मंज़ूरी बाकी है - उसे अभी तक पूरा या अस्वीकार नहीं किया गया है
  • सेटल किया गया - उसे पूरा या अस्वीकार किया गया है

खास बातें इसके अलावा, प्रॉमिस जैसी दिखने वाले ऑब्जेक्ट के बारे में बताने के लिए, thenable शब्द का भी इस्तेमाल किया जाता है, में एक then तरीका है. यह शब्द मुझे इंग्लैंड के पुराने फ़ुटबॉल मैच की याद दिलाता है मैनेजर टेरी वेनेबल्स ने ऐसा किया मैं इसका कम से कम इस्तेमाल करूंगा.

प्रॉमिस JavaScript में उपलब्ध होता है!

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

ऊपर दिए गए JavaScript वादों में एक सामान्य और तय स्टैंडर्ड वाला तरीका इस्तेमाल होता है. प्रॉमिस/A+ कहा जाता है. अगर आपने आप एक jQuery उपयोगकर्ता हैं, और उसका नाम कुछ मिलता-जुलता है स्थगित. हालांकि, स्थगित किए गए आइटम, Promise/A+ के मुताबिक नहीं होते हैं. इससे वे अलग-अलग और कम उपयोगी, तो सावधान रहें. jQuery में यह भी है प्रॉमिस टाइप, लेकिन यह सिर्फ़ 'स्थगित' का सबसेट है और इसमें एक जैसी समस्याएं हैं.

हालांकि, वादे लागू करने के तरीकों का पालन स्टैंडर्ड के मुताबिक किया जाता है, लेकिन कुल एपीआई अलग-अलग होते हैं. JavaScript के प्रॉमिस, एपीआई में response.js की मदद से मिलते-जुलते होते हैं. यहां प्रॉमिस तैयार करने का तरीका बताया गया है:

var promise = new Promise(function(resolve, reject) {
  // do a thing, possibly async, then…

  if (/* everything turned out fine */) {
    resolve("Stuff worked!");
  }
  else {
    reject(Error("It broke"));
  }
});

प्रॉमिस कंस्ट्रक्टर एक तर्क लेता है, यानी दो पैरामीटर वाला कॉलबैक, समाधान और अस्वीकार करें. कॉलबैक में कुछ करें. ऐसा हो सकता है कि यह एक साथ काम न करे. इसके बाद, कॉल करें अगर सब कुछ ठीक से काम करता है, तो समस्या का समाधान करें, नहीं तो कॉल अस्वीकार कर दिया जाता है.

सामान्य पुराने JavaScript के throw की तरह, यह इस्तेमाल करने के लिए पारंपरिक है, लेकिन ज़रूरी नहीं है. अस्वीकार करें. गड़बड़ी वाले ऑब्जेक्ट का यह फ़ायदा होता है कि वे स्टैक ट्रेस, डीबग करने वाले टूल को ज़्यादा मददगार बनाता है.

यहां बताया गया है कि इस प्रॉमिस का इस्तेमाल कैसे किया जाता है:

promise.then(function(result) {
  console.log(result); // "Stuff worked!"
}, function(err) {
  console.log(err); // Error: "It broke"
});

then() दो आर्ग्युमेंट लेता है. पहला, सक्सेस केस के लिए कॉलबैक, और दूसरा का इस्तेमाल किया जा सकता है. दोनों वैकल्पिक हैं, इसलिए आप सिर्फ़ सफलता या असफलता के बारे में बताएं.

JavaScript के प्रॉमिस को डीओएम में "फ़्यूचर्स" के तौर पर शुरू किया गया था. इसका नाम बदलकर "प्रॉमिसेस" कर दिया गया है, आखिर में उसे JavaScript पर ले जाया गया. आपके पास उन्हें JavaScript में रखने के बजाय DOM बेहतरीन है क्योंकि वे गैर-ब्राउज़र JS कॉन्टेक्स्ट में उपलब्ध होंगे, जैसे कि Node.js के लिए यह ज़रूरी है कि वे अपने मुख्य एपीआई में इनका इस्तेमाल करें.

हालांकि, ये JavaScript की सुविधा होती है, लेकिन DOM उनका इस्तेमाल करने से डरता नहीं है. तय सीमा में असल में, एक साथ काम नहीं करने वाले सक्सेस या फ़ेलर के तरीकों वाले सभी नए डीओएम एपीआई, प्रॉमिस का इस्तेमाल करेंगे. यह इवेंट पहले से ही कोटा मैनेजमेंट, फ़ॉन्ट लोड इवेंट, ServiceWorker, वेब एमआईडीआई, स्ट्रीम वगैरह.

अन्य लाइब्रेरी के साथ काम करना

JavaScript का वादा करने वाला एपीआई, then() तरीके का इस्तेमाल करने पर किसी भी चीज़ को इस तरह इस्तेमाल करेगा प्रॉमिस जैसा (या प्रॉमिस-स्पीक आह में thenable), ताकि किसी लाइब्रेरी का इस्तेमाल किया जा सके यह एक Q प्रॉमिस देता है. इसमें कोई दिक्कत नहीं है, यह JavaScript वादा करता है.

हालांकि, जैसा कि मैंने बताया, jQuery के डिफ़र्ड नहीं हैं ... काम के नहीं हैं. शुक्र है कि आप उन्हें सामान्य वादों में कास्ट कर सकते हैं, जो कि उनके लिए काफ़ी फ़ायदेमंद होगा जल्द से जल्द:

var jsPromise = Promise.resolve($.ajax('/whatever.json'))

यहां, jQuery का $.ajax 'स्थगित' दिखाता है. इसकी वजह then() है, Promise.resolve() इसे JavaScript प्रॉमिस में बदल सकता है. हालांकि, कभी-कभी डिफ़र्ड होने की वजह से, उनके कॉलबैक में कई आर्ग्युमेंट पास किए जाते हैं, उदाहरण के लिए:

var jqDeferred = $.ajax('/whatever.json');

jqDeferred.then(function(response, statusText, xhrObj) {
  // ...
}, function(xhrObj, textStatus, err) {
  // ...
})

वहीं JS पहले को छोड़कर सभी को अनदेखा करने का वादा करता है:

jsPromise.then(function(response) {
  // ...
}, function(xhrObj) {
  // ...
})

अच्छी बात यह है कि आम तौर पर आपको यह तरीका चाहिए या इससे कम से कम आपको जो आपको चाहिए. साथ ही, ध्यान रखें कि jQuery गड़बड़ी की जानकारी देने वाले ऑब्जेक्ट को अस्वीकार कर दिया जाता है.

जटिल एसिंक कोड अब आसान हो गया है

चलिए, कुछ चीज़ों के लिए कोड डालते हैं. मान लें कि हम:

  1. लोड होने के बारे में बताने के लिए, स्पिनर शुरू करें
  2. किसी कहानी के लिए कुछ JSON फ़ेच करें, जिनसे हमें हर चैप्टर का टाइटल और यूआरएल मिलते हैं
  3. पेज पर टाइटल जोड़ें
  4. हर चैप्टर फ़ेच करें
  5. पेज पर कहानी जोड़ें
  6. स्पिनर रोकें

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

बेशक, आप किसी खबर को डिलीवर करने के लिए JavaScript का इस्तेमाल नहीं करेंगे, ज़्यादा तेज़ी से काम करता है, लेकिन एपीआई के साथ काम करते समय यह पैटर्न काफ़ी आम है: एक से ज़्यादा डेटा फ़ेच करता है, फिर ये सब हो जाने पर कुछ करें.

सबसे पहले, आइए नेटवर्क से डेटा फ़ेच करने के बारे में बात करें:

XMLHttpRequest का वादा करना

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

function get(url) {
  // Return a new promise.
  return new Promise(function(resolve, reject) {
    // Do the usual XHR stuff
    var req = new XMLHttpRequest();
    req.open('GET', url);

    req.onload = function() {
      // This is called even on 404 etc
      // so check the status
      if (req.status == 200) {
        // Resolve the promise with the response text
        resolve(req.response);
      }
      else {
        // Otherwise reject with the status text
        // which will hopefully be a meaningful error
        reject(Error(req.statusText));
      }
    };

    // Handle network errors
    req.onerror = function() {
      reject(Error("Network Error"));
    };

    // Make the request
    req.send();
  });
}

अब इसका इस्तेमाल करते हैं:

get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.error("Failed!", error);
})

अब हम मैन्युअल तरीके से XMLHttpRequest लिखे बिना एचटीटीपी अनुरोध कर सकते हैं. यह बहुत अच्छी बात है, क्योंकि मुझे XMLHttpRequest का भयानक दूत के आकार को नहीं देखना पड़ेगा, तो मेरा जीवन उतना ही सुखी होगा.

चेन बनाना

then() कहानी का अंत नहीं है. आप then को एक साथ जोड़कर वैल्यू को ट्रांसफ़ॉर्म करना या एक के बाद एक अतिरिक्त ऐक्शन चलाना.

वैल्यू बदलना

आप बस नई वैल्यू दिखाकर, वैल्यू को बदल सकते हैं:

var promise = new Promise(function(resolve, reject) {
  resolve(1);
});

promise.then(function(val) {
  console.log(val); // 1
  return val + 2;
}).then(function(val) {
  console.log(val); // 3
})

एक व्यावहारिक उदाहरण के रूप में, आइए वापस चलते हैं:

get('story.json').then(function(response) {
  console.log("Success!", response);
})

रिस्पॉन्स, JSON फ़ॉर्मैट में है. हालांकि, यह अभी सादे टेक्स्ट के तौर पर मिला है. बुध JSON का इस्तेमाल करने के लिए हमारे गेट फ़ंक्शन में बदलाव हो सकता है responseType हालांकि, हम इसे वादों के हिसाब से भी हल कर सकते हैं:

get('story.json').then(function(response) {
  return JSON.parse(response);
}).then(function(response) {
  console.log("Yey JSON!", response);
})

चूंकि JSON.parse() एक तर्क लेता है और एक रूपांतरित मान देता है, तो हम उसका शॉर्टकट बना सकते हैं:

get('story.json').then(JSON.parse).then(function(response) {
  console.log("Yey JSON!", response);
})

असल में, हम getJSON() फ़ंक्शन को बहुत आसानी से बना सकते हैं:

function getJSON(url) {
  return get(url).then(JSON.parse);
}

getJSON() अब भी प्रॉमिस लौटाता है. प्रॉमिस प्रॉमिस होता है, जो यूआरएल फ़ेच करता है और फिर पार्स करता है JSON फ़ॉर्मैट में जवाब दिया गया है.

एसिंक्रोनस कार्रवाइयों को सूची में जोड़ना

एक साथ काम नहीं करने वाली कार्रवाइयों को क्रम में चलाने के लिए, then को चेन भी किया जा सकता है.

जब आप then() कॉलबैक से कोई आइटम लौटाते हैं, तो वह जादू की तरह हो जाता है. अगर आपने कोई वैल्यू दी है, तो उस वैल्यू के साथ अगले then() को कॉल किया जाता है. हालांकि, अगर आप वादे की तरह कुछ रिटर्न करते हैं, तो अगला then() उस पर इंतज़ार करता है और सिर्फ़ तब कॉल किया जाता है, जब वह वायदा पूरा हो जाता है (पूरा होता है/फ़ेल होता है). उदाहरण के लिए:

getJSON('story.json').then(function(story) {
  return getJSON(story.chapterUrls[0]);
}).then(function(chapter1) {
  console.log("Got chapter 1!", chapter1);
})

यहां हम story.json को एक साथ काम नहीं करने वाला अनुरोध करते हैं, जिससे हमें यूआरएल का अनुरोध किया जाता है, तो हम उनमें से पहले यूआरएल के लिए अनुरोध करते हैं. ऐसा तब होता है, जब कोई वादा किया जाता है वे सरल कॉलबैक पैटर्न से अलग दिखते हैं.

चैप्टर पाने के लिए, शॉर्टकट का तरीका भी बनाया जा सकता है:

var storyPromise;

function getChapter(i) {
  storyPromise = storyPromise || getJSON('story.json');

  return storyPromise.then(function(story) {
    return getJSON(story.chapterUrls[i]);
  })
}

// and using it is simple:
getChapter(0).then(function(chapter) {
  console.log(chapter);
  return getChapter(1);
}).then(function(chapter) {
  console.log(chapter);
})

जब तक getChapter को कॉल नहीं किया जाता, तब तक हम story.json को डाउनलोड नहीं करते. हालांकि, अगले समय getChapter के तौर पर हम स्टोरी में किए गए प्रॉमिस का दोबारा इस्तेमाल करते हैं. इसलिए, story.json को सिर्फ़ एक बार फ़ेच किया जाता है. वाह प्रॉमिस्स!

गड़बड़ी ठीक करना

जैसा कि हमने पहले देखा, then() दो तर्क देता है, एक सफलता के लिए दूसरा गलती के लिए (या वादों को पूरा करने और अस्वीकार करने के लिए):

get('story.json').then(function(response) {
  console.log("Success!", response);
}, function(error) {
  console.log("Failed!", error);
})

catch() का भी इस्तेमाल किया जा सकता है:

get('story.json').then(function(response) {
  console.log("Success!", response);
}).catch(function(error) {
  console.log("Failed!", error);
})

catch() में कुछ भी खास नहीं है, यह केवल चीनी है then(undefined, func), लेकिन आसानी से पढ़ा जा सकता है. ध्यान दें कि दोनों कोड ऊपर दिए गए उदाहरण जैसा व्यवहार नहीं करते हैं, बाद वाला इसके बराबर है:

get('story.json').then(function(response) {
  console.log("Success!", response);
}).then(undefined, function(error) {
  console.log("Failed!", error);
})

अंतर बहुत कम है, लेकिन बेहद काम का है. प्रॉमिस अस्वीकार होना छोड़ें अगले then() पर फ़ॉरवर्ड करें (या catch(), क्योंकि यह समान है). then(func1, func2) के साथ, func1 या func2 होगी कहा जाता है, दोनों नहीं. हालांकि, then(func1).catch(func2) के साथ दोनों अगर func1 अस्वीकार करता है, तो कॉल किया जाता है, क्योंकि वे चेन के अलग-अलग चरण हैं. इससे जाएं: निम्न:

asyncThing1().then(function() {
  return asyncThing2();
}).then(function() {
  return asyncThing3();
}).catch(function(err) {
  return asyncRecovery1();
}).then(function() {
  return asyncThing4();
}, function(err) {
  return asyncRecovery2();
}).catch(function(err) {
  console.log("Don't worry about it");
}).then(function() {
  console.log("All done!");
})

ऊपर दिया गया फ़्लो, सामान्य JavaScript 'ट्राई/कैच करें' से काफ़ी मिलता-जुलता है. "कोशिश करें" में ही हो catch() ब्लॉक पर तुरंत जाएं. यह रही ऊपर एक फ़्लोचार्ट की तरह (क्योंकि मुझे फ़्लोचार्ट पसंद हैं):

जिन वादों को पूरा करना है उनके लिए नीली रेखाओं का पालन करें या जिन वादों को पूरा करना है उनके लिए लाल लाइन देखें अस्वीकार करें.

JavaScript अपवाद और प्रॉमिस

प्रॉमिस तब अस्वीकार किया जाता है, जब प्रॉमिस को साफ़ तौर पर अस्वीकार कर दिया जाता है. हालांकि, दावे को साफ़ तौर पर अस्वीकार किया जाता है अगर कंस्ट्रक्टर कॉलबैक में कोई गड़बड़ी होती है:

var jsonPromise = new Promise(function(resolve, reject) {
  // JSON.parse throws an error if you feed it some
  // invalid JSON, so this implicitly rejects:
  resolve(JSON.parse("This ain't JSON"));
});

jsonPromise.then(function(data) {
  // This never happens:
  console.log("It worked!", data);
}).catch(function(err) {
  // Instead, this happens:
  console.log("It failed!", err);
})

इसका मतलब यह है कि यह यह कंस्ट्रक्टर कॉलबैक का वादा करता है, ताकि गड़बड़ियां अपने-आप पकड़ी जाएं और अस्वीकार हो जाते हैं.

then() कॉलबैक में होने वाली गड़बड़ियों पर भी यही बात लागू होती है.

get('/').then(JSON.parse).then(function() {
  // This never happens, '/' is an HTML page, not JSON
  // so JSON.parse throws
  console.log("It worked!", data);
}).catch(function(err) {
  // Instead, this happens:
  console.log("It failed!", err);
})

गड़बड़ी को ठीक करना

अपनी कहानी और चैप्टर के साथ, हम उपयोगकर्ता को कोई गड़बड़ी दिखाने के लिए कैच करने की सुविधा का इस्तेमाल कर सकते हैं:

getJSON('story.json').then(function(story) {
  return getJSON(story.chapterUrls[0]);
}).then(function(chapter1) {
  addHtmlToPage(chapter1.html);
}).catch(function() {
  addTextToPage("Failed to show chapter");
}).then(function() {
  document.querySelector('.spinner').style.display = 'none';
})

अगर story.chapterUrls[0] फ़ेच नहीं हो पाता है (जैसे, http 500 या उपयोगकर्ता ऑफ़लाइन है), तो यह आगे के सभी सक्सेस कॉलबैक को स्किप कर देगा, जिनमें वाला कॉलबैक शामिल है getJSON(), जो रिस्पॉन्स को JSON के तौर पर पार्स करने की कोशिश करता है. साथ ही, यह एक कॉलबैक है, जो पेज पर चैप्टर1.html जोड़ता है. इसके बजाय यह कैच पर ले जाता है कॉलबैक. इस वजह से, "चैप्टर नहीं दिखाया जा सका" पेज पर जोड़ा जाएगा, अगर इनमें से कोई भी कार्रवाई पूरी नहीं हो सकी.

JavaScript के 'आज़माएं/कैच करें' की तरह ही, गड़बड़ी का पता चलने पर और बाद वाला कोड भी आ जाता है जारी रहता है, इसलिए स्पिनर हमेशा छिपा रहता है, और हम यही चाहते हैं. कॉन्टेंट बनाने ऊपर दिया गया वर्शन, इसका नॉन-ब्लॉक करने वाला एसिंक्रोनस वर्शन बन जाता है:

try {
  var story = getJSONSync('story.json');
  var chapter1 = getJSONSync(story.chapterUrls[0]);
  addHtmlToPage(chapter1.html);
}
catch (e) {
  addTextToPage("Failed to show chapter");
}
document.querySelector('.spinner').style.display = 'none'

सिर्फ़ लॉग करने के लिए, catch() का इस्तेमाल करें और वह भी बिना रिकवर किए चुनें. ऐसा करने के लिए, बस गड़बड़ी को फिर से ठीक करें. हम इतने समय में ऐसा कर सकते हैं हमारी getJSON() विधि:

function getJSON(url) {
  return get(url).then(JSON.parse).catch(function(err) {
    console.log("getJSON failed for", url, err);
    throw err;
  });
}

इसलिए, हम एक चैप्टर फ़ेच कर पाए, लेकिन हमें सभी चैप्टर चाहिए. आइए, बनाते हैं होता है.

समानता और क्रम: दोनों का बेहतर तरीके से इस्तेमाल करना

एक साथ कई काम करना आसान नहीं है. अगर आपको सफलता पाने में परेशानी हो रही है, कोड को ऐसे लिखने का प्रयास करें, जैसे कि वह सिंक्रोनस था. इस मामले में:

try {
  var story = getJSONSync('story.json');
  addHtmlToPage(story.heading);

  story.chapterUrls.forEach(function(chapterUrl) {
    var chapter = getJSONSync(chapterUrl);
    addHtmlToPage(chapter.html);
  });

  addTextToPage("All done");
}
catch (err) {
  addTextToPage("Argh, broken: " + err.message);
}

document.querySelector('.spinner').style.display = 'none'

सही रहेगा! हालांकि, यह सिंक होता है और चीज़ें डाउनलोड होने के दौरान ब्राउज़र को लॉक कर देता है. यहां की यात्रा पर हूं एक के बाद एक काम करने के लिए, हम then() का इस्तेमाल करते हैं.

getJSON('story.json').then(function(story) {
  addHtmlToPage(story.heading);

  // TODO: for each url in story.chapterUrls, fetch & display
}).then(function() {
  // And we're all done!
  addTextToPage("All done");
}).catch(function(err) {
  // Catch any error that happened along the way
  addTextToPage("Argh, broken: " + err.message);
}).then(function() {
  // Always hide the spinner
  document.querySelector('.spinner').style.display = 'none';
})

हालांकि, हम चैप्टर के यूआरएल को लूप में चलाकर, उन्हें क्रम से कैसे फ़ेच कर सकते हैं? यह काम नहीं करता है:

story.chapterUrls.forEach(function(chapterUrl) {
  // Fetch chapter
  getJSON(chapterUrl).then(function(chapter) {
    // and add it to the page
    addHtmlToPage(chapter.html);
  });
})

forEach के बारे में जानकारी मौजूद नहीं है. इसलिए, हमारे चैप्टर कुछ भी क्रम में दिखेंगे वे डाउनलोड करते हैं, जो मूल रूप से Pulp फ़िक्शन कैसे लिखे गए थे. यह नहीं है Pulp फ़िक्शन, तो चलिए इसे ठीक करते हैं.

क्रम बनाना

हमें अपने chapterUrls कलेक्शन को प्रॉमिस के क्रम में बदलना है. हम then() का इस्तेमाल करके ऐसा कर सकते हैं:

// Start off with a promise that always resolves
var sequence = Promise.resolve();

// Loop through our chapter urls
story.chapterUrls.forEach(function(chapterUrl) {
  // Add these actions to the end of the sequence
  sequence = sequence.then(function() {
    return getJSON(chapterUrl);
  }).then(function(chapter) {
    addHtmlToPage(chapter.html);
  });
})

हमने पहली बार Promise.resolve() देखा है, जिससे ऐसा प्रॉमिस पाना जो आपकी दी हुई किसी भी वैल्यू के मुताबिक हो. अगर आपने इसे Promise का इंस्टेंस यह बस इसे लौटा देगा (नोट: यह बदलें, तो कुछ सुविधाएं अभी तक लागू नहीं हुई हैं). अगर आपको इसे प्रॉमिस जैसा कुछ पास करते हैं (इसमें then() तरीका होता है), तो यह असली Promise जो एक ही तरह से पूरा/अस्वीकार करता है. अगर आपने पास कर लिया किसी दूसरे मान में, जैसे कि Promise.resolve('Hello'), इससे वादा करता है जो उस मूल्य को पूरा करता है. अगर इसे बिना किसी वैल्यू के कॉल किया जाए, जैसा कि ऊपर बताया गया है, यह "तय नहीं है" से पूरा करता है.

एक ऐसा प्रॉमिस भी है, जोPromise.reject(val) वह मान होता है, जो आप उसे देते हैं (या अनिर्धारित).

हम ऊपर दिए गए कोड का इस्तेमाल करके, array.reduce:

// Loop through our chapter urls
story.chapterUrls.reduce(function(sequence, chapterUrl) {
  // Add these actions to the end of the sequence
  return sequence.then(function() {
    return getJSON(chapterUrl);
  }).then(function(chapter) {
    addHtmlToPage(chapter.html);
  });
}, Promise.resolve())

यह पिछले उदाहरण की तरह ही है, लेकिन इसमें अलग "सीक्वेंस" वैरिएबल. कलेक्शन में मौजूद हर आइटम के लिए, कम करने के कॉलबैक का इस्तेमाल किया जाता है. "सीक्वेंस" पहली बार Promise.resolve() है, लेकिन बाकी के लिए "क्रम" कॉल करता है वह है जो हमने पिछले कॉल में लौटाया है. array.reduce अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है अरे को सिंगल वैल्यू तक उबालने में मदद करता है. इस मामले में यह वादा है.

आइए, इसे एक जगह इकट्ठा करते हैं:

getJSON('story.json').then(function(story) {
  addHtmlToPage(story.heading);

  return story.chapterUrls.reduce(function(sequence, chapterUrl) {
    // Once the last chapter's promise is done…
    return sequence.then(function() {
      // …fetch the next chapter
      return getJSON(chapterUrl);
    }).then(function(chapter) {
      // and add it to the page
      addHtmlToPage(chapter.html);
    });
  }, Promise.resolve());
}).then(function() {
  // And we're all done!
  addTextToPage("All done");
}).catch(function(err) {
  // Catch any error that happened along the way
  addTextToPage("Argh, broken: " + err.message);
}).then(function() {
  // Always hide the spinner
  document.querySelector('.spinner').style.display = 'none';
})

इसमें, सिंक वर्शन का पूरी तरह से एक साथ काम न करने वाला वर्शन उपलब्ध है. हालांकि, हम यह कर सकते हैं किया है. अभी हमारा पेज इस तरह से डाउनलोड हो रहा है:

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

Promise.all(arrayOfPromises).then(function(arrayOfResults) {
  //...
})

Promise.all, अलग-अलग तरह के वादों को पूरा करता है और ऐसा वादा करता है जिसे पूरा करना होगा और इन सभी को पूरा कर लिया जाता है. तो आपको नतीजों की एक पूरी श्रेणी (जो भी हो) मिलती है पूरे किए गए वादों के हिसाब से.

getJSON('story.json').then(function(story) {
  addHtmlToPage(story.heading);

  // Take an array of promises and wait on them all
  return Promise.all(
    // Map our array of chapter urls to
    // an array of chapter json promises
    story.chapterUrls.map(getJSON)
  );
}).then(function(chapters) {
  // Now we have the chapters jsons in order! Loop through…
  chapters.forEach(function(chapter) {
    // …and add to the page
    addHtmlToPage(chapter.html);
  });
  addTextToPage("All done");
}).catch(function(err) {
  // catch any error that happened so far
  addTextToPage("Argh, broken: " + err.message);
}).then(function() {
  document.querySelector('.spinner').style.display = 'none';
})

कनेक्शन के हिसाब से, यह एक-एक करके लोड होने के मुकाबले कुछ सेकंड तेज़ हो सकता है, और यह हमारी पहली कोशिश की तुलना में कम कोड है. चैप्टर किसी भी फ़ॉर्मैट में डाउनलोड किए जा सकते हैं लेकिन वे स्क्रीन पर सही क्रम में दिखते हैं.

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

ऐसा करने के लिए, हम अपने सभी चैप्टर से एक ही समय पर JSON फ़ेच करते हैं. इसके बाद, अनुक्रम:

getJSON('story.json')
.then(function(story) {
  addHtmlToPage(story.heading);

  // Map our array of chapter urls to
  // an array of chapter json promises.
  // This makes sure they all download in parallel.
  return story.chapterUrls.map(getJSON)
    .reduce(function(sequence, chapterPromise) {
      // Use reduce to chain the promises together,
      // adding content to the page for each chapter
      return sequence
      .then(function() {
        // Wait for everything in the sequence so far,
        // then wait for this chapter to arrive.
        return chapterPromise;
      }).then(function(chapter) {
        addHtmlToPage(chapter.html);
      });
    }, Promise.resolve());
}).then(function() {
  addTextToPage("All done");
}).catch(function(err) {
  // catch any error that happened along the way
  addTextToPage("Argh, broken: " + err.message);
}).then(function() {
  document.querySelector('.spinner').style.display = 'none';
})

और चलिए, दोनों में से बेहतरीन को अपनाते हैं! इसे डिलीवर करने में उतना ही समय लगता है सारे कॉन्टेंट के लिए उपलब्ध है, लेकिन कॉन्टेंट का शुरुआती हिस्सा उपयोगकर्ता को पहले ही मिल जाता है.

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

ऊपर दिया गया तरीका Node.js-style कॉलबैक के साथ या इवेंट आस-पास है कोड को दोगुना कर दिया जाता है, लेकिन सबसे अहम बात यह है कि इसे फ़ॉलो करना भी आसान नहीं होता. हालांकि, इस ES6 की अन्य सुविधाओं के साथ जोड़ने पर, वादे के बारे में बताने वाली कहानी खत्म नहीं होगी तो वे और भी आसान हो जाते हैं.

बोनस राउंड: ज़्यादा सुविधाएं

जब से मैंने इस लेख को लिखा था, तब से प्रॉमिसेस का इस्तेमाल करने की क्षमता बढ़ गई है काफ़ी अच्छी तरह से मदद कर सकते हैं. Chrome 55 के बाद से, एसिंक फ़ंक्शन ने प्रॉमिस-आधारित कोड को पर इस तरह लिखा जाएगा जैसे कि वह सिंक्रोनस हो, लेकिन मुख्य थ्रेड को ब्लॉक न करे. आप इसके बारे में ज़्यादा जानने के लिए, एक साथ काम करने वाले मेरे फ़ंक्शन से जुड़े लेख पढ़ें. यहां कई बड़े ब्राउज़र में, प्रॉमिस और एक साथ काम न करने वाले फ़ंक्शन, दोनों के लिए बड़े पैमाने पर सहायता मिलती है. आपको एमडीएन में जानकारी मिल जाएगी वादा और एक साथ काम नहीं करने वाली प्रोसेस फ़ंक्शन संदर्भ.

इसमें ऐन वैन केस्टेरेन, डोमेनिक डेनिकोला, टॉम ऐशवर्थ, रेमी शार्प, ऐडी उस्मानी, आर्थर इवांस, और युताका हिरानो ने इसे प्रूफ़रीड किया और बनाया सुझाव या सुधार.

साथ ही, इस इवेंट के लिए मैथियास बाइनेंस को धन्यवाद कई हिस्सों को अपडेट किया जा रहा है लेख की समीक्षा करें.