जावास्क्रिप्ट में एसिंक्रोनस प्रोग्रामिंग का परिचय
JavaScript एक सिंगल-थ्रेडेड प्रोग्रामिंग भाषा है जिसमें नॉन-ब्लॉकिंग इवेंट-ड्रिवन मॉडल है। Node.js वातावरण में, एसिंक्रोनस प्रोग्रामिंग केवल एक सुविधाजनक विकल्प नहीं है, बल्कि एक मौलिक आवश्यकता है। एसिंक्रोनसिटी के बिना, सर्वर एप्लिकेशन डेटाबेस क्वेरी, फ़ाइल रीडिंग या नेटवर्क कॉल के हर अनुरोध पर ब्लॉक हो जाएगा, जिससे यह वास्तविक उपयोग के लिए अनुपयुक्त हो जाएगा।
इस गाइड में, हम जावास्क्रिप्ट में एसिंक्रोनस प्रोग्रामिंग के तीन मुख्य प्रतिमानों को समझेंगे: Callback, Promise और async/await। आप सीखेंगे कि Event Loop कैसे काम करता है, त्रुटियों को कैसे संभालें, और Node.js के लिए पठनीय, उच्च-प्रदर्शन कोड कैसे लिखें।
1. Callback: एसिंक्रोनसिटी का आधार
Callback (कॉलबैक फ़ंक्शन) एक ऐसा फ़ंक्शन है जिसे किसी अन्य फ़ंक्शन में तर्क के रूप में पास किया जाता है और एसिंक्रोनस ऑपरेशन पूरा होने के बाद कॉल किया जाता है। यह जावास्क्रिप्ट में एसिंक्रोनसिटी का सबसे पुराना और सबसे बुनियादी मॉडल है।
Callback के साथ एक सरल उदाहरण
const fs = require('fs');
// कॉलबैक के साथ एसिंक्रोनस फ़ाइल रीडिंगfs.readFile('example.txt', 'utf8', (err, data) => { if (err) { console.error('फ़ाइल पढ़ने में त्रुटि:', err); return; } console.log('फ़ाइल की सामग्री:', data);});
console.log('फ़ाइल पढ़ना शुरू किया गया...');// आउटपुट: "फ़ाइल पढ़ना शुरू किया गया..." फ़ाइल की सामग्री से पहले दिखाई देगाइस उदाहरण में, कॉलबैक फ़ंक्शन तभी निष्पादित होगा जब फ़ाइल पूरी तरह से पढ़ ली जाएगी। इस बीच, मुख्य थ्रेड निष्पादन जारी रखता है — यही नॉन-ब्लॉकिंग व्यवहार है।
Callback Hell (कॉलबैक नरक)
कॉलबैक का मुख्य दोष नेस्टेड कॉल में पठनीयता का तेजी से नुकसान है:
// कॉलबैक नरकgetUser(1, (err, user) => { if (err) throw err; getPosts(user.id, (err, posts) => { if (err) throw err; getComments(posts[0].id, (err, comments) => { if (err) throw err; console.log(user.name, posts[0].title, comments); }); });});ऐसे कोड को डीबग करना, बनाए रखना और विस्तारित करना मुश्किल है। सौभाग्य से, आधुनिक जावास्क्रिप्ट कहीं अधिक सुरुचिपूर्ण समाधान प्रदान करता है।
2. Promise: परिणाम का वादा
Promise (प्रॉमिस) एक ऑब्जेक्ट है जो किसी एसिंक्रोनस ऑपरेशन के परिणाम का प्रतिनिधित्व करता है, जो पूरा (fulfilled), अस्वीकृत (rejected) या लंबित (pending) हो सकता है। प्रॉमिस नेस्टिंग से बचने और कॉल चेन बनाने की अनुमति देते हैं।
Promise का निर्माण और उपयोग
// फ़ंक्शन जो Promise लौटाता हैfunction readFilePromise(filename) { return new Promise((resolve, reject) => { const fs = require('fs'); fs.readFile(filename, 'utf8', (err, data) => { if (err) { reject(err); // त्रुटि — प्रॉमिस अस्वीकृत } else { resolve(data); // सफलता — प्रॉमिस पूरा } }); });}
// प्रॉमिस का उपयोगreadFilePromise('example.txt') .then(data => { console.log('सामग्री:', data); return data.length; }) .then(length => { console.log('टेक्स्ट की लंबाई:', length); }) .catch(err => { console.error('त्रुटि:', err); });Promise.all और Promise.race के तरीके
Promise.all सभी प्रॉमिस के पूरा होने की प्रतीक्षा करने की अनुमति देता है, जबकि Promise.race पहले पूरे हुए प्रॉमिस की प्रतीक्षा करता है:
const p1 = readFilePromise('file1.txt');const p2 = readFilePromise('file2.txt');const p3 = readFilePromise('file3.txt');
// तीनों फ़ाइलों की प्रतीक्षा करेंPromise.all([p1, p2, p3]) .then(([data1, data2, data3]) => { console.log('सभी फ़ाइलें लोड हो गईं'); console.log(data1, data2, data3); }) .catch(err => console.error('किसी एक फ़ाइल में त्रुटि:', err));
// पहले पूरे हुए की प्रतीक्षा करेंPromise.race([p1, p2, p3]) .then(firstResult => { console.log('पहली फ़ाइल लोड हुई:', firstResult); });