ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ನಲ್ಲಿ ಆಧುನಿಕ ಸ್ಟ್ರೀಮ್ ಪ್ರೊಸೆಸಿಂಗ್ ಅನ್ನು ಕರಗತ ಮಾಡಿಕೊಳ್ಳಿ. ಈ ಸಮಗ್ರ ಮಾರ್ಗದರ್ಶಿ ಪರಿಣಾಮಕಾರಿ ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ನಿರ್ವಹಣೆಗಾಗಿ ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳು ಮತ್ತು 'for await...of' ಲೂಪ್ ಅನ್ನು ಪರಿಶೋಧಿಸುತ್ತದೆ.
ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ ಅಸಿಂಕ್ ಇಟರೇಟರ್ ಸ್ಟ್ರೀಮ್ ಕಂಟ್ರೋಲ್: ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ನಿರ್ವಹಣೆಯ ಆಳವಾದ ಅಧ್ಯಯನ
ಆಧುನಿಕ ಸಾಫ್ಟ್ವೇರ್ ಅಭಿವೃದ್ಧಿಯ ಜಗತ್ತಿನಲ್ಲಿ, ಡೇಟಾ ಎಂಬುದು ಹೊಸ ಇಂಧನವಾಗಿದೆ, ಮತ್ತು ಅದು ಸಾಮಾನ್ಯವಾಗಿ ಪ್ರವಾಹದಂತೆ ಹರಿಯುತ್ತದೆ. ನೀವು ಬೃಹತ್ ಲಾಗ್ ಫೈಲ್ಗಳನ್ನು ಪ್ರೊಸೆಸ್ ಮಾಡುತ್ತಿರಲಿ, ರಿಯಲ್-ಟೈಮ್ API ಫೀಡ್ಗಳನ್ನು ಬಳಸುತ್ತಿರಲಿ, ಅಥವಾ ಬಳಕೆದಾರರ ಅಪ್ಲೋಡ್ಗಳನ್ನು ನಿರ್ವಹಿಸುತ್ತಿರಲಿ, ಡೇಟಾ ಸ್ಟ್ರೀಮ್ಗಳನ್ನು ಸಮರ್ಥವಾಗಿ ನಿರ್ವಹಿಸುವ ಸಾಮರ್ಥ್ಯವು ಇನ್ನು ಮುಂದೆ ಒಂದು ಸಣ್ಣ ಕೌಶಲ್ಯವಲ್ಲ—ಅದೊಂದು ಅವಶ್ಯಕತೆಯಾಗಿದೆ. ಸ್ಟ್ರೀಮ್ ಪ್ರೊಸೆಸಿಂಗ್ನಲ್ಲಿನ ಅತ್ಯಂತ ನಿರ್ಣಾಯಕ ಸವಾಲುಗಳಲ್ಲಿ ಒಂದು ವೇಗದ ಉತ್ಪಾದಕ (producer) ಮತ್ತು ನಿಧಾನಗತಿಯ ಗ್ರಾಹಕ (consumer) ನಡುವಿನ ಡೇಟಾ ಹರಿವನ್ನು ನಿರ್ವಹಿಸುವುದು. ಇದನ್ನು ನಿಯಂತ್ರಿಸದಿದ್ದರೆ, ಈ ಅಸಮತೋಲನವು ಅತಿಯಾದ ಮೆಮೊರಿ ಬಳಕೆಗೆ, ಅಪ್ಲಿಕೇಶನ್ ಕ್ರ್ಯಾಶ್ಗಳಿಗೆ, ಮತ್ತು ಕಳಪೆ ಬಳಕೆದಾರ ಅನುಭವಕ್ಕೆ ಕಾರಣವಾಗಬಹುದು.
ಇಲ್ಲೇ ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ಕಾರ್ಯರೂಪಕ್ಕೆ ಬರುತ್ತದೆ. ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ಎಂಬುದು ಒಂದು ರೀತಿಯ ಫ್ಲೋ ಕಂಟ್ರೋಲ್ ಆಗಿದ್ದು, ಇದರಲ್ಲಿ ಗ್ರಾಹಕನು ತಾನು ಡೇಟಾವನ್ನು ಎಷ್ಟು ವೇಗವಾಗಿ ಪ್ರೊಸೆಸ್ ಮಾಡಬಲ್ಲನೋ ಅಷ್ಟೇ ವೇಗವಾಗಿ ಸ್ವೀಕರಿಸುವುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಲು, ಉತ್ಪಾದಕನಿಗೆ ನಿಧಾನಗೊಳಿಸಲು ಸಂಕೇತ ನೀಡಬಹುದು. ಹಲವು ವರ್ಷಗಳ ಕಾಲ, ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ನಲ್ಲಿ ದೃಢವಾದ ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ಅನ್ನು ಅಳವಡಿಸುವುದು ಸಂಕೀರ್ಣವಾಗಿತ್ತು, ಅದಕ್ಕೆ RxJS ನಂತಹ ಥರ್ಡ್-ಪಾರ್ಟಿ ಲೈಬ್ರರಿಗಳು ಅಥವಾ ಸಂಕೀರ್ಣ ಕಾಲ್ಬ್ಯಾಕ್-ಆಧಾರಿತ ಸ್ಟ್ರೀಮ್ APIಗಳು ಬೇಕಾಗುತ್ತಿದ್ದವು.
ಅದೃಷ್ಟವಶಾತ್, ಆಧುನಿಕ ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ ಭಾಷೆಯಲ್ಲೇ ನಿರ್ಮಿಸಲಾದ ಒಂದು ಶಕ್ತಿಯುತ ಮತ್ತು ಸೊಗಸಾದ ಪರಿಹಾರವನ್ನು ಒದಗಿಸುತ್ತದೆ: ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳು. for await...of ಲೂಪ್ನೊಂದಿಗೆ ಸೇರಿ, ಈ ವೈಶಿಷ್ಟ್ಯವು ಸ್ಟ್ರೀಮ್ಗಳನ್ನು ನಿರ್ವಹಿಸಲು ಮತ್ತು ಪೂರ್ವನಿಯೋಜಿತವಾಗಿ ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ಅನ್ನು ನಿರ್ವಹಿಸಲು ಒಂದು ಸಹಜ, ಅರ್ಥಗರ್ಭಿತ ಮಾರ್ಗವನ್ನು ಒದಗಿಸುತ್ತದೆ. ಈ ಲೇಖನವು ಈ ಮಾದರಿಯ ಆಳವಾದ ಅಧ್ಯಯನವಾಗಿದೆ, ಇದು ನಿಮ್ಮನ್ನು ಮೂಲಭೂತ ಸಮಸ್ಯೆಯಿಂದ ಪ್ರಾರಂಭಿಸಿ, ಸ್ಥಿತಿಸ್ಥಾಪಕ, ಮೆಮೊರಿ-ಸಮರ್ಥ, ಮತ್ತು ಸ್ಕೇಲೆಬಲ್ ಡೇಟಾ-ಚಾಲಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ನಿರ್ಮಿಸಲು ಸುಧಾರಿತ ಮಾದರಿಗಳವರೆಗೆ ಮಾರ್ಗದರ್ಶಿಸುತ್ತದೆ.
ಮೂಲ ಸಮಸ್ಯೆಯನ್ನು ಅರ್ಥಮಾಡಿಕೊಳ್ಳುವುದು: ಡೇಟಾ ಪ್ರವಾಹ
ಪರಿಹಾರವನ್ನು ಸಂಪೂರ್ಣವಾಗಿ ಪ್ರಶಂಸಿಸಲು, ನಾವು ಮೊದಲು ಸಮಸ್ಯೆಯನ್ನು ಅರ್ಥಮಾಡಿಕೊಳ್ಳಬೇಕು. ಒಂದು ಸರಳ ಸನ್ನಿವೇಶವನ್ನು ಕಲ್ಪಿಸಿಕೊಳ್ಳಿ: ನಿಮ್ಮ ಬಳಿ ಒಂದು ದೊಡ್ಡ ಟೆಕ್ಸ್ಟ್ ಫೈಲ್ (ಹಲವಾರು ಗಿಗಾಬೈಟ್ಗಳು) ಇದೆ ಮತ್ತು ನೀವು ಅದರಲ್ಲಿ ಒಂದು ನಿರ್ದಿಷ್ಟ ಪದದ ಸಂಭವಿಸುವಿಕೆಯನ್ನು ಎಣಿಸಬೇಕಾಗಿದೆ. ಒಂದು ಸರಳ ವಿಧಾನವೆಂದರೆ ಸಂಪೂರ್ಣ ಫೈಲ್ ಅನ್ನು ಒಂದೇ ಬಾರಿಗೆ ಮೆಮೊರಿಗೆ ಓದುವುದು.
ದೊಡ್ಡ ಪ್ರಮಾಣದ ಡೇಟಾಗೆ ಹೊಸಬರಾದ ಡೆವಲಪರ್ Node.js ಪರಿಸರದಲ್ಲಿ ಈ ರೀತಿ ಏನಾದರೂ ಬರೆಯಬಹುದು:
// WARNING: Do NOT run this on a very large file!
const fs = require('fs');
function countWordInFile(filePath, word) {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error('Error reading file:', err);
return;
}
const count = (data.match(new RegExp(`\b${word}\b`, 'gi')) || []).length;
console.log(`The word "${word}" appears ${count} times.`);
});
}
// This will crash if 'large-file.txt' is bigger than available RAM.
countWordInFile('large-file.txt', 'error');
ಈ ಕೋಡ್ ಸಣ್ಣ ಫೈಲ್ಗಳಿಗೆ ಸಂಪೂರ್ಣವಾಗಿ ಕೆಲಸ ಮಾಡುತ್ತದೆ. ಆದಾಗ್ಯೂ, large-file.txt 5GB ಇದ್ದು, ನಿಮ್ಮ ಸರ್ವರ್ನಲ್ಲಿ ಕೇವಲ 2GB of RAM ಇದ್ದರೆ, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ ಮೆಮೊರಿ ಮೀರಿದ ದೋಷದಿಂದ (out-of-memory error) ಕ್ರ್ಯಾಶ್ ಆಗುತ್ತದೆ. ಉತ್ಪಾದಕ (ಫೈಲ್ ಸಿಸ್ಟಮ್) ಸಂಪೂರ್ಣ ಫೈಲ್ನ ವಿಷಯವನ್ನು ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ಗೆ ಸುರಿಯುತ್ತದೆ, ಮತ್ತು ಗ್ರಾಹಕ (ನಿಮ್ಮ ಕೋಡ್) ಅದನ್ನು ಒಂದೇ ಬಾರಿಗೆ ನಿಭಾಯಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ.
ಇದು ಶ್ರೇಷ್ಠ ಉತ್ಪಾದಕ-ಗ್ರಾಹಕ ಸಮಸ್ಯೆ (producer-consumer problem). ಉತ್ಪಾದಕನು ಗ್ರಾಹಕ ಪ್ರೊಸೆಸ್ ಮಾಡುವುದಕ್ಕಿಂತ ವೇಗವಾಗಿ ಡೇಟಾವನ್ನು ಉತ್ಪಾದಿಸುತ್ತಾನೆ. ಅವುಗಳ ನಡುವಿನ ಬಫರ್—ಈ ಸಂದರ್ಭದಲ್ಲಿ, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ನ ಮೆಮೊರಿ—ತುಂಬಿ ತುಳುಕುತ್ತದೆ. ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ಎಂಬುದು ಗ್ರಾಹಕನು ಉತ್ಪಾದಕನಿಗೆ, "ತಡಿ, ನೀನು ಕಳುಹಿಸಿದ ಕೊನೆಯ ಡೇಟಾದ ಮೇಲೆ ನಾನು ಇನ್ನೂ ಕೆಲಸ ಮಾಡುತ್ತಿದ್ದೇನೆ. ನಾನು ಕೇಳುವವರೆಗೂ ಇನ್ನಷ್ಟು ಕಳುಹಿಸಬೇಡ." ಎಂದು ಹೇಳಲು ಅನುವು ಮಾಡಿಕೊಡುವ ಯಾಂತ್ರಿಕತೆಯಾಗಿದೆ.
ಅಸಿಂಕ್ರೊನಸ್ ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ನ ವಿಕಸನ: ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳೆಡೆಗಿನ ದಾರಿ
ಅಸಿಂಕ್ರೊನಸ್ ಕಾರ್ಯಾಚರಣೆಗಳೊಂದಿಗಿನ ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ನ ಪ್ರಯಾಣವು ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳು ಏಕೆ ಇಷ್ಟು ಮಹತ್ವದ ವೈಶಿಷ್ಟ್ಯವಾಗಿವೆ ಎಂಬುದಕ್ಕೆ ನಿರ್ಣಾಯಕ ಸಂದರ್ಭವನ್ನು ಒದಗಿಸುತ್ತದೆ.
- ಕಾಲ್ಬ್ಯಾಕ್ಗಳು: ಮೂಲ ಯಾಂತ್ರಿಕತೆ. ಶಕ್ತಿಯುತವಾಗಿದ್ದರೂ "ಕಾಲ್ಬ್ಯಾಕ್ ಹೆಲ್" ಅಥವಾ "ಪಿರಾಮಿಡ್ ಆಫ್ ಡೂಮ್,"ಗೆ ಕಾರಣವಾಯಿತು, ಇದು ಕೋಡ್ ಅನ್ನು ಓದಲು ಮತ್ತು ನಿರ್ವಹಿಸಲು ಕಷ್ಟಕರವಾಗಿಸಿತು. ಫ್ಲೋ ಕಂಟ್ರೋಲ್ ಹಸ್ತಚಾಲಿತ ಮತ್ತು ದೋಷಪೂರಿತವಾಗಿತ್ತು.
- ಪ್ರಾಮಿಸಸ್: ಒಂದು ಪ್ರಮುಖ ಸುಧಾರಣೆ, ಭವಿಷ್ಯದ ಮೌಲ್ಯವನ್ನು ಪ್ರತಿನಿಧಿಸುವ ಮೂಲಕ ಅಸಿಂಕ್ ಕಾರ್ಯಾಚರಣೆಗಳನ್ನು ನಿಭಾಯಿಸಲು ಸ್ವಚ್ಛವಾದ ಮಾರ್ಗವನ್ನು ಪರಿಚಯಿಸಿತು.
.then()ಜೊತೆಗಿನ ಚೈನಿಂಗ್ ಕೋಡ್ ಅನ್ನು ಹೆಚ್ಚು ರೇಖೀಯವಾಗಿಸಿತು, ಮತ್ತು.catch()ಉತ್ತಮ ದೋಷ ನಿರ್ವಹಣೆಯನ್ನು ಒದಗಿಸಿತು. ಆದಾಗ್ಯೂ, ಪ್ರಾಮಿಸಸ್ಗಳು ಉತ್ಸುಕವಾಗಿವೆ (eager)—ಅವು ಕಾಲಾನಂತರದಲ್ಲಿ ನಿರಂತರವಾದ ಮೌಲ್ಯಗಳ ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಪ್ರತಿನಿಧಿಸುವುದಿಲ್ಲ, ಬದಲಿಗೆ ಒಂದೇ, ಅಂತಿಮ ಮೌಲ್ಯವನ್ನು ಪ್ರತಿನಿಧಿಸುತ್ತವೆ. - Async/Await: ಪ್ರಾಮಿಸಸ್ಗಳ ಮೇಲಿನ ಸಿಂಟ್ಯಾಕ್ಟಿಕ್ ಶುಗರ್, ಇದು ಡೆವಲಪರ್ಗಳಿಗೆ ಸಿಂಕ್ರೊನಸ್ ಕೋಡ್ನಂತೆ ಕಾಣುವ ಮತ್ತು ವರ್ತಿಸುವ ಅಸಿಂಕ್ರೊನಸ್ ಕೋಡ್ ಬರೆಯಲು ಅನುವು ಮಾಡಿಕೊಟ್ಟಿತು. ಇದು ಓದುವಿಕೆಯನ್ನು ಗಣನೀಯವಾಗಿ ಸುಧಾರಿಸಿತು ಆದರೆ, ಪ್ರಾಮಿಸಸ್ಗಳಂತೆ, ಇದು ಮೂಲಭೂತವಾಗಿ ಒಂದು-ಬಾರಿಯ ಅಸಿಂಕ್ ಕಾರ್ಯಾಚರಣೆಗಳಿಗಾಗಿ ವಿನ್ಯಾಸಗೊಳಿಸಲಾಗಿದೆ, ಸ್ಟ್ರೀಮ್ಗಳಿಗಲ್ಲ.
Node.js ದೀರ್ಘಕಾಲದಿಂದ ತನ್ನ ಸ್ಟ್ರೀಮ್ಸ್ API ಅನ್ನು ಹೊಂದಿದ್ದರೂ, ಅದು ಆಂತರಿಕ ಬಫರಿಂಗ್ ಮತ್ತು .pause()/.resume() ವಿಧಾನಗಳ ಮೂಲಕ ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ಅನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ, ಆದರೆ ಇದು ಕಡಿದಾದ ಕಲಿಕೆಯ ರೇಖೆಯನ್ನು ಮತ್ತು ವಿಶಿಷ್ಟವಾದ API ಅನ್ನು ಹೊಂದಿದೆ. ಸರಳವಾದ ಅರೇಯನ್ನು ಇಟರೇಟ್ ಮಾಡುವಷ್ಟೇ ಸುಲಭವಾಗಿ ಮತ್ತು ಸ್ಪಷ್ಟವಾಗಿ ಅಸಿಂಕ್ರೊನಸ್ ಡೇಟಾದ ಸ್ಟ್ರೀಮ್ಗಳನ್ನು ನಿರ್ವಹಿಸಲು ಭಾಷಾ-ಸಹಜವಾದ ಮಾರ್ಗವು ಕಾಣೆಯಾಗಿತ್ತು. ಈ ಅಂತರವನ್ನು ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳು ತುಂಬುತ್ತವೆ.
ಇಟರೇಟರ್ಗಳು ಮತ್ತು ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳ ಕುರಿತು ಒಂದು ಪ್ರೈಮರ್
ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳನ್ನು ಕರಗತ ಮಾಡಿಕೊಳ್ಳಲು, ಮೊದಲು ಅವುಗಳ ಸಿಂಕ್ರೊನಸ್ ಪ್ರತಿರೂಪಗಳ ಬಗ್ಗೆ ದೃಢವಾದ ತಿಳುವಳಿಕೆಯನ್ನು ಹೊಂದುವುದು ಸಹಾಯಕವಾಗಿದೆ.
ಸಿಂಕ್ರೊನಸ್ ಇಟರೇಟರ್ ಪ್ರೋಟೋಕಾಲ್
ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ನಲ್ಲಿ, ಒಂದು ಆಬ್ಜೆಕ್ಟ್ ಇಟರೇಟರ್ ಪ್ರೋಟೋಕಾಲ್ ಅನ್ನು ಅಳವಡಿಸಿಕೊಂಡಿದ್ದರೆ ಅದನ್ನು ಇಟರೇಬಲ್ (iterable) ಎಂದು ಪರಿಗಣಿಸಲಾಗುತ್ತದೆ. ಇದರರ್ಥ ಆಬ್ಜೆಕ್ಟ್ Symbol.iterator ಕೀ ಮೂಲಕ ಪ್ರವೇಶಿಸಬಹುದಾದ ಒಂದು ಮೆಥಡ್ ಅನ್ನು ಹೊಂದಿರಬೇಕು. ಈ ಮೆಥಡ್ ಅನ್ನು ಕರೆದಾಗ, ಅದು ಒಂದು ಇಟರೇಟರ್ ಆಬ್ಜೆಕ್ಟ್ ಅನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ.
ಇಟರೇಟರ್ ಆಬ್ಜೆಕ್ಟ್, ಪ್ರತಿಯಾಗಿ, next() ಮೆಥಡ್ ಅನ್ನು ಹೊಂದಿರಬೇಕು. next() ಗೆ ಪ್ರತಿ ಕರೆಯು ಎರಡು ಪ್ರಾಪರ್ಟಿಗಳೊಂದಿಗೆ ಒಂದು ಆಬ್ಜೆಕ್ಟ್ ಅನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ:
value: ಅನುಕ್ರಮದಲ್ಲಿನ ಮುಂದಿನ ಮೌಲ್ಯ.done: ಅನುಕ್ರಮವು ಮುಗಿದಿದ್ದರೆtrueಆಗಿರುವ ಮತ್ತು ಇಲ್ಲದಿದ್ದರೆfalseಆಗಿರುವ ಬೂಲಿಯನ್.
for...of ಲೂಪ್ ಈ ಪ್ರೋಟೋಕಾಲ್ಗೆ ಸಿಂಟ್ಯಾಕ್ಟಿಕ್ ಶುಗರ್ ಆಗಿದೆ. ಒಂದು ಸರಳ ಉದಾಹರಣೆಯನ್ನು ನೋಡೋಣ:
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
let nextIndex = start;
const rangeIterator = {
next() {
if (nextIndex < end) {
const result = { value: nextIndex, done: false };
nextIndex += step;
return result;
} else {
return { value: undefined, done: true };
}
}
};
return rangeIterator;
}
const it = makeRangeIterator(1, 4);
console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { value: undefined, done: true }
ಅಸಿಂಕ್ರೊನಸ್ ಇಟರೇಟರ್ ಪ್ರೋಟೋಕಾಲ್ ಅನ್ನು ಪರಿಚಯಿಸುವುದು
ಅಸಿಂಕ್ರೊನಸ್ ಇಟರೇಟರ್ ಪ್ರೋಟೋಕಾಲ್ ಅದರ ಸಿಂಕ್ರೊನಸ್ ಸೋದರ ಸಂಬಂಧಿಯ ಒಂದು ಸಹಜ ವಿಸ್ತರಣೆಯಾಗಿದೆ. ಪ್ರಮುಖ ವ್ಯತ್ಯಾಸಗಳು:
- ಇಟರೇಬಲ್ ಆಬ್ಜೆಕ್ಟ್
Symbol.asyncIteratorಮೂಲಕ ಪ್ರವೇಶಿಸಬಹುದಾದ ಒಂದು ಮೆಥಡ್ ಅನ್ನು ಹೊಂದಿರಬೇಕು. - ಇಟರೇಟರ್ನ
next()ಮೆಥಡ್{ value, done }ಆಬ್ಜೆಕ್ಟ್ಗೆ ರಿಸಾಲ್ವ್ ಆಗುವ ಒಂದು ಪ್ರಾಮಿಸ್ (Promise) ಅನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ.
ಈ ಸರಳ ಬದಲಾವಣೆ—ಫಲಿತಾಂಶವನ್ನು ಪ್ರಾಮಿಸ್ನಲ್ಲಿ ಸುತ್ತುವುದು—ನಂಬಲಾಗದಷ್ಟು ಶಕ್ತಿಯುತವಾಗಿದೆ. ಇದರರ್ಥ ಇಟರೇಟರ್ ಮುಂದಿನ ಮೌಲ್ಯವನ್ನು ತಲುಪಿಸುವ ಮೊದಲು ಅಸಿಂಕ್ರೊನಸ್ ಕೆಲಸವನ್ನು (ನೆಟ್ವರ್ಕ್ ವಿನಂತಿ ಅಥವಾ ಡೇಟಾಬೇಸ್ ಪ್ರಶ್ನೆಯಂತಹ) ಮಾಡಬಹುದು. ಅಸಿಂಕ್ ಇಟರೇಬಲ್ಗಳನ್ನು ಬಳಸಲು ಅನುಗುಣವಾದ ಸಿಂಟ್ಯಾಕ್ಟಿಕ್ ಶುಗರ್ for await...of ಲೂಪ್ ಆಗಿದೆ.
ಪ್ರತಿ ಸೆಕೆಂಡಿಗೆ ಒಂದು ಮೌಲ್ಯವನ್ನು ಹೊರಸೂಸುವ ಒಂದು ಸರಳ ಅಸಿಂಕ್ ಇಟರೇಟರ್ ಅನ್ನು ರಚಿಸೋಣ:
const myAsyncIterable = {
[Symbol.asyncIterator]() {
let i = 0;
return {
next() {
if (i < 5) {
return new Promise(resolve => {
setTimeout(() => {
resolve({ value: i++, done: false });
}, 1000);
});
} else {
return Promise.resolve({ done: true });
}
}
};
}
};
// Consuming the async iterable
(async () => {
for await (const value of myAsyncIterable) {
console.log(value); // Logs 0, 1, 2, 3, 4, one per second
}
})();
for await...of ಲೂಪ್ ಪ್ರತಿ ಇಟರೇಶನ್ನಲ್ಲಿ ತನ್ನ ಕಾರ್ಯಗತಗೊಳಿಸುವಿಕೆಯನ್ನು ಹೇಗೆ ವಿರಾಮಗೊಳಿಸುತ್ತದೆ ಎಂಬುದನ್ನು ಗಮನಿಸಿ, next() ನಿಂದ ಹಿಂತಿರುಗಿದ ಪ್ರಾಮಿಸ್ ರಿಸಾಲ್ವ್ ಆಗುವವರೆಗೆ ಕಾಯುತ್ತದೆ. ಈ ವಿರಾಮಗೊಳಿಸುವ ಯಾಂತ್ರಿಕತೆಯೇ ಬ್ಯಾಕ್ಪ್ರೆಶರ್ನ ಅಡಿಪಾಯವಾಗಿದೆ.
ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳೊಂದಿಗೆ ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ಕ್ರಿಯೆಯಲ್ಲಿ
ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳ ಮ್ಯಾಜಿಕ್ ಏನೆಂದರೆ ಅವು ಪುಲ್-ಆಧಾರಿತ ವ್ಯವಸ್ಥೆಯನ್ನು (pull-based system) ಅಳವಡಿಸುತ್ತವೆ. ಗ್ರಾಹಕ (for await...of ಲೂಪ್) ನಿಯಂತ್ರಣದಲ್ಲಿರುತ್ತಾನೆ. ಇದು .next() ಅನ್ನು ಕರೆಯುವ ಮೂಲಕ ಮುಂದಿನ ಡೇಟಾದ ತುಣುಕನ್ನು ಸ್ಪಷ್ಟವಾಗಿ *ಎಳೆಯುತ್ತದೆ (pulls)* ಮತ್ತು ನಂತರ ಕಾಯುತ್ತದೆ. ಉತ್ಪಾದಕನು ಗ್ರಾಹಕನು ವಿನಂತಿಸುವುದಕ್ಕಿಂತ ವೇಗವಾಗಿ ಡೇಟಾವನ್ನು ತಳ್ಳಲು (push) ಸಾಧ್ಯವಿಲ್ಲ. ಇದು ಭಾಷೆಯ ಸಿಂಟ್ಯಾಕ್ಸ್ನಲ್ಲೇ ನಿರ್ಮಿಸಲಾದ ಸಹಜವಾದ ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ಆಗಿದೆ.
ಉದಾಹರಣೆ: ಬ್ಯಾಕ್ಪ್ರೆಶರ್-ಅರಿತ ಫೈಲ್ ಪ್ರೊಸೆಸರ್
ನಮ್ಮ ಫೈಲ್-ಎಣಿಕೆಯ ಸಮಸ್ಯೆಯನ್ನು ಮತ್ತೆ ನೋಡೋಣ. ಆಧುನಿಕ Node.js ಸ್ಟ್ರೀಮ್ಗಳು (v10 ರಿಂದ) ಸಹಜವಾಗಿ ಅಸಿಂಕ್ ಇಟರೇಬಲ್ ಆಗಿವೆ. ಇದರರ್ಥ ನಾವು ನಮ್ಮ ವಿಫಲವಾದ ಕೋಡ್ ಅನ್ನು ಕೆಲವೇ ಸಾಲುಗಳಲ್ಲಿ ಮೆಮೊರಿ-ಸಮರ್ಥವಾಗಿ ಪುನಃ ಬರೆಯಬಹುದು:
import { createReadStream } from 'fs';
import { Writable } from 'stream';
async function processLargeFile(filePath) {
const readableStream = createReadStream(filePath, { highWaterMark: 64 * 1024 }); // 64KB chunks
console.log('Starting file processing...');
// The for await...of loop consumes the stream
for await (const chunk of readableStream) {
// The producer (file system) is paused here. It will not read the next
// chunk from the disk until this block of code finishes its execution.
console.log(`Processing a chunk of size: ${chunk.length} bytes.`);
// Simulate a slow consumer operation (e.g., writing to a slow database or API)
await new Promise(resolve => setTimeout(resolve, 500));
}
console.log('File processing complete. Memory usage remained low.');
}
processLargeFile('very-large-file.txt').catch(console.error);
ಇದು ಏಕೆ ಕೆಲಸ ಮಾಡುತ್ತದೆ ಎಂಬುದನ್ನು ವಿಂಗಡಿಸೋಣ:
createReadStreamಒಂದು ಓದಬಲ್ಲ ಸ್ಟ್ರೀಮ್ ಅನ್ನು ರಚಿಸುತ್ತದೆ, ಅದು ಒಂದು ಉತ್ಪಾದಕವಾಗಿದೆ. ಇದು ಸಂಪೂರ್ಣ ಫೈಲ್ ಅನ್ನು ಒಂದೇ ಬಾರಿಗೆ ಓದುವುದಿಲ್ಲ. ಇದು ಒಂದು ತುಣುಕನ್ನು ಆಂತರಿಕ ಬಫರ್ಗೆ ಓದುತ್ತದೆ (highWaterMarkವರೆಗೆ).for await...ofಲೂಪ್ ಪ್ರಾರಂಭವಾಗುತ್ತದೆ. ಇದು ಸ್ಟ್ರೀಮ್ನ ಆಂತರಿಕnext()ಮೆಥಡ್ ಅನ್ನು ಕರೆಯುತ್ತದೆ, ಇದು ಡೇಟಾದ ಮೊದಲ ತುಣುಕಿಗಾಗಿ ಪ್ರಾಮಿಸ್ ಅನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ.- ಮೊದಲ ತುಣುಕು ಲಭ್ಯವಾದ ನಂತರ, ಲೂಪ್ನ ಬಾಡಿ ಕಾರ್ಯಗತಗೊಳ್ಳುತ್ತದೆ. ಲೂಪ್ನೊಳಗೆ, ನಾವು
awaitಬಳಸಿ 500ms ವಿಳಂಬದೊಂದಿಗೆ ನಿಧಾನವಾದ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ಅನುಕರಿಸುತ್ತೇವೆ. - ಇದು ನಿರ್ಣಾಯಕ ಭಾಗ: ಲೂಪ್ `await` ಮಾಡುತ್ತಿರುವಾಗ, ಅದು ಸ್ಟ್ರೀಮ್ನ ಮೇಲೆ
next()ಅನ್ನು ಕರೆಯುವುದಿಲ್ಲ. ಉತ್ಪಾದಕ (ಫೈಲ್ ಸ್ಟ್ರೀಮ್) ಗ್ರಾಹಕನು ಕಾರ್ಯನಿರತನಾಗಿದ್ದಾನೆ ಮತ್ತು ಅದರ ಆಂತರಿಕ ಬಫರ್ ತುಂಬಿದೆ ಎಂದು ನೋಡುತ್ತದೆ, ಆದ್ದರಿಂದ ಅದು ಫೈಲ್ನಿಂದ ಓದುವುದನ್ನು ನಿಲ್ಲಿಸುತ್ತದೆ. ಆಪರೇಟಿಂಗ್ ಸಿಸ್ಟಂನ ಫೈಲ್ ಹ್ಯಾಂಡಲ್ ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗುತ್ತದೆ. ಇದೇ ಕ್ರಿಯೆಯಲ್ಲಿರುವ ಬ್ಯಾಕ್ಪ್ರೆಶರ್. - 500ms ನಂತರ, `await` ಪೂರ್ಣಗೊಳ್ಳುತ್ತದೆ. ಲೂಪ್ ತನ್ನ ಮೊದಲ ಇಟರೇಶನ್ ಅನ್ನು ಮುಗಿಸುತ್ತದೆ ಮತ್ತು ಮುಂದಿನ ತುಣುಕನ್ನು ವಿನಂತಿಸಲು ತಕ್ಷಣವೇ
next()ಅನ್ನು ಮತ್ತೆ ಕರೆಯುತ್ತದೆ. ಉತ್ಪಾದಕನಿಗೆ ಪುನರಾರಂಭಿಸಲು ಸಂಕೇತ ಸಿಗುತ್ತದೆ ಮತ್ತು ಡಿಸ್ಕ್ನಿಂದ ಮುಂದಿನ ತುಣುಕನ್ನು ಓದುತ್ತದೆ.
ಫೈಲ್ ಸಂಪೂರ್ಣವಾಗಿ ಓದುವವರೆಗೂ ಈ ಚಕ್ರವು ಮುಂದುವರಿಯುತ್ತದೆ. ಯಾವುದೇ ಹಂತದಲ್ಲಿ ಸಂಪೂರ್ಣ ಫೈಲ್ ಅನ್ನು ಮೆಮೊರಿಗೆ ಲೋಡ್ ಮಾಡಲಾಗುವುದಿಲ್ಲ. ನಾವು ಪ್ರತಿ ಬಾರಿಯೂ ಕೇವಲ ಒಂದು ಸಣ್ಣ ತುಣುಕನ್ನು ಮಾತ್ರ ಸಂಗ್ರಹಿಸುತ್ತೇವೆ, ಇದು ನಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ನ ಮೆಮೊರಿ ಬಳಕೆಯನ್ನು ಫೈಲ್ ಗಾತ್ರವನ್ನು ಲೆಕ್ಕಿಸದೆ ಚಿಕ್ಕದಾಗಿ ಮತ್ತು ಸ್ಥಿರವಾಗಿರಿಸುತ್ತದೆ.
ಸುಧಾರಿತ ಸನ್ನಿವೇಶಗಳು ಮತ್ತು ಮಾದರಿಗಳು
ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳ ನಿಜವಾದ ಶಕ್ತಿಯು ನೀವು ಅವುಗಳನ್ನು ಸಂಯೋಜಿಸಲು ಪ್ರಾರಂಭಿಸಿದಾಗ, ಘೋಷಣಾತ್ಮಕ, ಓದಬಲ್ಲ, ಮತ್ತು ಸಮರ್ಥ ಡೇಟಾ ಸಂಸ್ಕರಣಾ ಪೈಪ್ಲೈನ್ಗಳನ್ನು ರಚಿಸಿದಾಗ ಅನಾವರಣಗೊಳ್ಳುತ್ತದೆ.
ಅಸಿಂಕ್ ಜನರೇಟರ್ಗಳೊಂದಿಗೆ ಸ್ಟ್ರೀಮ್ಗಳನ್ನು ಪರಿವರ್ತಿಸುವುದು
ಒಂದು ಅಸಿಂಕ್ ಜನರೇಟರ್ ಫಂಕ್ಷನ್ (async function* ()) ಟ್ರಾನ್ಸ್ಫಾರ್ಮರ್ಗಳನ್ನು ರಚಿಸಲು ಪರಿಪೂರ್ಣ ಸಾಧನವಾಗಿದೆ. ಇದು ಅಸಿಂಕ್ ಇಟರೇಬಲ್ ಅನ್ನು ಬಳಸಬಲ್ಲ ಮತ್ತು ಉತ್ಪಾದಿಸಬಲ್ಲ ಒಂದು ಫಂಕ್ಷನ್ ಆಗಿದೆ.
ನಾವು ಟೆಕ್ಸ್ಟ್ ಡೇಟಾದ ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಓದುವ, ಪ್ರತಿ ಲೈನ್ ಅನ್ನು JSON ಆಗಿ ಪಾರ್ಸ್ ಮಾಡುವ, ಮತ್ತು ನಂತರ ಒಂದು ನಿರ್ದಿಷ್ಟ ಸ್ಥಿತಿಯನ್ನು ಪೂರೈಸುವ ರೆಕಾರ್ಡ್ಗಳಿಗಾಗಿ ಫಿಲ್ಟರ್ ಮಾಡುವ ಪೈಪ್ಲೈನ್ ನಮಗೆ ಬೇಕು ಎಂದು ಕಲ್ಪಿಸಿಕೊಳ್ಳಿ. ನಾವು ಇದನ್ನು ಸಣ್ಣ, ಮರುಬಳಕೆ ಮಾಡಬಹುದಾದ ಅಸಿಂಕ್ ಜನರೇಟರ್ಗಳೊಂದಿಗೆ ನಿರ್ಮಿಸಬಹುದು.
// Generator 1: Takes a stream of chunks and yields lines
async function* chunksToLines(chunkAsyncIterable) {
let previous = '';
for await (const chunk of chunkAsyncIterable) {
previous += chunk;
let eolIndex;
while ((eolIndex = previous.indexOf('\n')) >= 0) {
const line = previous.slice(0, eolIndex + 1);
yield line;
previous = previous.slice(eolIndex + 1);
}
}
if (previous.length > 0) {
yield previous;
}
}
// Generator 2: Takes a stream of lines and yields parsed JSON objects
async function* parseJSON(stringAsyncIterable) {
for await (const line of stringAsyncIterable) {
try {
yield JSON.parse(line);
} catch (e) {
// Decide how to handle malformed JSON
console.error('Skipping invalid JSON line:', line);
}
}
}
// Generator 3: Filters objects based on a predicate
async function* filter(asyncIterable, predicate) {
for await (const value of asyncIterable) {
if (predicate(value)) {
yield value;
}
}
}
// Putting it all together to create a pipeline
async function main() {
const sourceStream = createReadStream('large-log-file.ndjson');
const lines = chunksToLines(sourceStream);
const objects = parseJSON(lines);
const importantEvents = filter(objects, (event) => event.level === 'error');
for await (const event of importantEvents) {
// This consumer is slow
await new Promise(resolve => setTimeout(resolve, 100));
console.log('Found an important event:', event);
}
}
main();
ಈ ಪೈಪ್ಲೈನ್ ಸುಂದರವಾಗಿದೆ. ಪ್ರತಿ ಹಂತವೂ ಪ್ರತ್ಯೇಕ, ಪರೀಕ್ಷಿಸಬಹುದಾದ ಘಟಕವಾಗಿದೆ. ಅದಕ್ಕಿಂತ ಮುಖ್ಯವಾಗಿ, ಸಂಪೂರ್ಣ ಸರಣಿಯುದ್ದಕ್ಕೂ ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ಅನ್ನು ಸಂರಕ್ಷಿಸಲಾಗಿದೆ. ಅಂತಿಮ ಗ್ರಾಹಕ (main ನಲ್ಲಿನ for await...of ಲೂಪ್) ನಿಧಾನವಾದರೆ, `filter` ಜನರೇಟರ್ ವಿರಾಮಗೊಳಿಸುತ್ತದೆ, ಇದು `parseJSON` ಜನರೇಟರ್ ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲು ಕಾರಣವಾಗುತ್ತದೆ, ಇದು `chunksToLines` ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲು ಕಾರಣವಾಗುತ್ತದೆ, ಇದು ಅಂತಿಮವಾಗಿ `createReadStream` ಗೆ ಡಿಸ್ಕ್ನಿಂದ ಓದುವುದನ್ನು ನಿಲ್ಲಿಸಲು ಸಂಕೇತ ನೀಡುತ್ತದೆ. ಒತ್ತಡವು ಗ್ರಾಹಕನಿಂದ ಉತ್ಪಾದಕನವರೆಗೆ ಸಂಪೂರ್ಣ ಪೈಪ್ಲೈನ್ ಮೂಲಕ ಹಿಂದಕ್ಕೆ ಪ್ರಸಾರವಾಗುತ್ತದೆ.
ಅಸಿಂಕ್ ಸ್ಟ್ರೀಮ್ಗಳಲ್ಲಿ ದೋಷಗಳನ್ನು ನಿಭಾಯಿಸುವುದು
ದೋಷ ನಿರ್ವಹಣೆ ಸರಳವಾಗಿದೆ. ನಿಮ್ಮ for await...of ಲೂಪ್ ಅನ್ನು try...catch ಬ್ಲಾಕ್ನಲ್ಲಿ ಸುತ್ತಬಹುದು. ಉತ್ಪಾದಕ ಅಥವಾ ರೂಪಾಂತರ ಪೈಪ್ಲೈನ್ನ ಯಾವುದೇ ಭಾಗವು ದೋಷವನ್ನು ಎಸೆದರೆ (ಅಥವಾ next() ನಿಂದ ತಿರಸ್ಕರಿಸಲ್ಪಟ್ಟ ಪ್ರಾಮಿಸ್ ಅನ್ನು ಹಿಂತಿರುಗಿಸಿದರೆ), ಅದನ್ನು ಗ್ರಾಹಕನ catch ಬ್ಲಾಕ್ ಹಿಡಿಯುತ್ತದೆ.
async function processWithErrors() {
try {
const stream = getStreamThatMightFail();
for await (const data of stream) {
console.log(data);
}
} catch (error) {
console.error('An error occurred during streaming:', error);
// Perform cleanup if necessary
}
}
ಸಂಪನ್ಮೂಲಗಳನ್ನು ಸರಿಯಾಗಿ ನಿರ್ವಹಿಸುವುದು ಸಹ ಮುಖ್ಯವಾಗಿದೆ. ಗ್ರಾಹಕನು ಲೂಪ್ನಿಂದ ಬೇಗನೆ ಹೊರಬರಲು ನಿರ್ಧರಿಸಿದರೆ (break ಅಥವಾ return ಬಳಸಿ), ಉತ್ತಮವಾಗಿ ವರ್ತಿಸುವ ಅಸಿಂಕ್ ಇಟರೇಟರ್ return() ಮೆಥಡ್ ಅನ್ನು ಹೊಂದಿರಬೇಕು. for await...of ಲೂಪ್ ಈ ಮೆಥಡ್ ಅನ್ನು ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಕರೆಯುತ್ತದೆ, ಇದು ಉತ್ಪಾದಕನಿಗೆ ಫೈಲ್ ಹ್ಯಾಂಡಲ್ಗಳು ಅಥವಾ ಡೇಟಾಬೇಸ್ ಸಂಪರ್ಕಗಳಂತಹ ಸಂಪನ್ಮೂಲಗಳನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ.
ನೈಜ-ಪ್ರಪಂಚದ ಬಳಕೆಯ ಪ್ರಕರಣಗಳು
ಅಸಿಂಕ್ ಇಟರೇಟರ್ ಮಾದರಿಯು ನಂಬಲಾಗದಷ್ಟು ಬಹುಮುಖವಾಗಿದೆ. ಇದು ಉತ್ತಮವಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುವ ಕೆಲವು ಸಾಮಾನ್ಯ ಜಾಗತಿಕ ಬಳಕೆಯ ಪ್ರಕರಣಗಳು ಇಲ್ಲಿವೆ:
- ಫೈಲ್ ಪ್ರೊಸೆಸಿಂಗ್ ಮತ್ತು ETL: ಎಕ್ಸ್ಟ್ರಾಕ್ಟ್, ಟ್ರಾನ್ಸ್ಫಾರ್ಮ್, ಲೋಡ್ (ETL) ಜಾಬ್ಗಳಿಗಾಗಿ ದೊಡ್ಡ CSVಗಳು, ಲಾಗ್ಗಳು (NDJSON ನಂತಹ), ಅಥವಾ XML ಫೈಲ್ಗಳನ್ನು ಅತಿಯಾದ ಮೆಮೊರಿ ಬಳಸದೆ ಓದುವುದು ಮತ್ತು ಪರಿವರ್ತಿಸುವುದು.
- ಪುಟೀಕರಿಸಿದ APIಗಳು: ಪುಟೀಕರಿಸಿದ API ಯಿಂದ (ಸಾಮಾಜಿಕ ಮಾಧ್ಯಮ ಫೀಡ್ ಅಥವಾ ಉತ್ಪನ್ನ ಕ್ಯಾಟಲಾಗ್ನಂತಹ) ಡೇಟಾವನ್ನು ತರುವ ಅಸಿಂಕ್ ಇಟರೇಟರ್ ಅನ್ನು ರಚಿಸುವುದು. ಗ್ರಾಹಕನು ಪುಟ 1 ಅನ್ನು ಪ್ರೊಸೆಸ್ ಮಾಡಿದ ನಂತರವೇ ಇಟರೇಟರ್ ಪುಟ 2 ಅನ್ನು ತರುತ್ತದೆ. ಇದು API ಮೇಲೆ ಅತಿಯಾದ ಒತ್ತಡವನ್ನು ತಡೆಯುತ್ತದೆ ಮತ್ತು ಮೆಮೊರಿ ಬಳಕೆಯನ್ನು ಕಡಿಮೆ ಮಾಡುತ್ತದೆ.
- ರಿಯಲ್-ಟೈಮ್ ಡೇಟಾ ಫೀಡ್ಗಳು: ವೆಬ್ಸಾಕೆಟ್ಗಳು, ಸರ್ವರ್-ಸೆಂಟ್ ಈವೆಂಟ್ಗಳು (SSE), ಅಥವಾ IoT ಸಾಧನಗಳಿಂದ ಡೇಟಾವನ್ನು ಬಳಸುವುದು. ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ ಲಾಜಿಕ್ ಅಥವಾ UI ಒಳಬರುವ ಸಂದೇಶಗಳ ಪ್ರವಾಹದಿಂದ ಮುಳುಗದಂತೆ ಖಚಿತಪಡಿಸುತ್ತದೆ.
- ಡೇಟಾಬೇಸ್ ಕರ್ಸರ್ಗಳು: ಡೇಟಾಬೇಸ್ನಿಂದ ಲಕ್ಷಾಂತರ ಸಾಲುಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡುವುದು. ಸಂಪೂರ್ಣ ಫಲಿತಾಂಶ ಸೆಟ್ ಅನ್ನು ತರುವ ಬದಲು, ಡೇಟಾಬೇಸ್ ಕರ್ಸರ್ ಅನ್ನು ಅಸಿಂಕ್ ಇಟರೇಟರ್ನಲ್ಲಿ ಸುತ್ತಬಹುದು, ಅಪ್ಲಿಕೇಶನ್ಗೆ ಬೇಕಾದಂತೆ ಬ್ಯಾಚ್ಗಳಲ್ಲಿ ಸಾಲುಗಳನ್ನು ತರಬಹುದು.
- ಅಂತರ್-ಸೇವಾ ಸಂವಹನ: ಮೈಕ್ರೋಸರ್ವಿಸಸ್ ಆರ್ಕಿಟೆಕ್ಚರ್ನಲ್ಲಿ, ಸೇವೆಗಳು gRPC ನಂತಹ ಪ್ರೋಟೋಕಾಲ್ಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಪರಸ್ಪರ ಡೇಟಾವನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಬಹುದು, ಇದು ಸಹಜವಾಗಿ ಸ್ಟ್ರೀಮಿಂಗ್ ಮತ್ತು ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ಅನ್ನು ಬೆಂಬಲಿಸುತ್ತದೆ, ಮತ್ತು ಇದನ್ನು ಹೆಚ್ಚಾಗಿ ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳಂತಹ ಮಾದರಿಗಳನ್ನು ಬಳಸಿ ಅಳವಡಿಸಲಾಗುತ್ತದೆ.
ಕಾರ್ಯಕ್ಷಮತೆಯ ಪರಿಗಣನೆಗಳು ಮತ್ತು ಉತ್ತಮ ಅಭ್ಯಾಸಗಳು
ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳು ಒಂದು ಶಕ್ತಿಯುತ ಸಾಧನವಾಗಿದ್ದರೂ, ಅವುಗಳನ್ನು ಬುದ್ಧಿವಂತಿಕೆಯಿಂದ ಬಳಸುವುದು ಮುಖ್ಯ.
- ಚಂಕ್ ಗಾತ್ರ ಮತ್ತು ಓವರ್ಹೆಡ್: ಪ್ರತಿ
awaitಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ ಎಂಜಿನ್ ಕಾರ್ಯಗತಗೊಳಿಸುವಿಕೆಯನ್ನು ವಿರಾಮಗೊಳಿಸಿ ಪುನರಾರಂಭಿಸುವುದರಿಂದ ಸಣ್ಣ ಪ್ರಮಾಣದ ಓವರ್ಹೆಡ್ ಅನ್ನು ಪರಿಚಯಿಸುತ್ತದೆ. ಅತಿ ಹೆಚ್ಚಿನ ಥ್ರೋಪುಟ್ ಸ್ಟ್ರೀಮ್ಗಳಿಗಾಗಿ, ಡೇಟಾವನ್ನು ಬೈಟ್-ಬೈ-ಬೈಟ್ ಅಥವಾ ಲೈನ್-ಬೈ-ಲೈನ್ ಪ್ರೊಸೆಸ್ ಮಾಡುವುದಕ್ಕಿಂತ ಸಮಂಜಸವಾದ ಗಾತ್ರದ ಚಂಕ್ಗಳಲ್ಲಿ (ಉದಾ., 64KB) ಪ್ರೊಸೆಸ್ ಮಾಡುವುದು ಹೆಚ್ಚು ಸಮರ್ಥವಾಗಿರುತ್ತದೆ. ಇದು ಲೇಟೆನ್ಸಿ ಮತ್ತು ಥ್ರೋಪುಟ್ ನಡುವಿನ ಒಂದು ಹೊಂದಾಣಿಕೆಯಾಗಿದೆ. - ನಿಯಂತ್ರಿತ ಸಮವರ್ತಿತನ:
for await...ofಮೂಲಕ ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ಸಹಜವಾಗಿ ಅನುಕ್ರಮವಾಗಿರುತ್ತದೆ. ನಿಮ್ಮ ಪ್ರೊಸೆಸಿಂಗ್ ಕಾರ್ಯಗಳು ಸ್ವತಂತ್ರವಾಗಿದ್ದು I/O-ಬೌಂಡ್ ಆಗಿದ್ದರೆ (ಪ್ರತಿ ಐಟಂಗೆ API ಕರೆ ಮಾಡುವಂತೆ), ನೀವು ನಿಯಂತ್ರಿತ ಸಮಾನಾಂತರತೆಯನ್ನು ಪರಿಚಯಿಸಲು ಬಯಸಬಹುದು. ನೀವುPromise.all()ಬಳಸಿ ಬ್ಯಾಚ್ಗಳಲ್ಲಿ ಐಟಂಗಳನ್ನು ಪ್ರೊಸೆಸ್ ಮಾಡಬಹುದು, ಆದರೆ ಡೌನ್ಸ್ಟ್ರೀಮ್ ಸೇವೆಯನ್ನು ಮುಳುಗಿಸುವ ಮೂಲಕ ಹೊಸ ಅಡಚಣೆಯನ್ನು ಸೃಷ್ಟಿಸದಂತೆ ಜಾಗರೂಕರಾಗಿರಿ. - ಸಂಪನ್ಮೂಲ ನಿರ್ವಹಣೆ: ನಿಮ್ಮ ಉತ್ಪಾದಕರು ಅನಿರೀಕ್ಷಿತವಾಗಿ ಮುಚ್ಚಲ್ಪಡುವುದನ್ನು ನಿಭಾಯಿಸಬಲ್ಲರು ಎಂದು ಯಾವಾಗಲೂ ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ. ಗ್ರಾಹಕನು ಬೇಗನೆ ನಿಲ್ಲಿಸಿದಾಗ ಸಂಪನ್ಮೂಲಗಳನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಲು (ಉದಾ., ಫೈಲ್ ಹ್ಯಾಂಡಲ್ಗಳನ್ನು ಮುಚ್ಚುವುದು, ನೆಟ್ವರ್ಕ್ ವಿನಂತಿಗಳನ್ನು ರದ್ದುಮಾಡುವುದು) ನಿಮ್ಮ ಕಸ್ಟಮ್ ಇಟರೇಟರ್ಗಳಲ್ಲಿ ಐಚ್ಛಿಕ
return()ಮೆಥಡ್ ಅನ್ನು ಅಳವಡಿಸಿ. - ಸರಿಯಾದ ಸಾಧನವನ್ನು ಆರಿಸಿ: ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳು ಕಾಲಾನಂತರದಲ್ಲಿ ಬರುವ ಮೌಲ್ಯಗಳ ಅನುಕ್ರಮವನ್ನು ನಿರ್ವಹಿಸಲು ಮೀಸಲಾಗಿವೆ. ನಿಮಗೆ ತಿಳಿದಿರುವ ಸಂಖ್ಯೆಯ ಸ್ವತಂತ್ರ ಅಸಿಂಕ್ ಕಾರ್ಯಗಳನ್ನು ಚಲಾಯಿಸಬೇಕಾದರೆ,
Promise.all()ಅಥವಾPromise.allSettled()ಇನ್ನೂ ಉತ್ತಮ ಮತ್ತು ಸರಳ ಆಯ್ಕೆಯಾಗಿದೆ.
ತೀರ್ಮಾನ: ಸ್ಟ್ರೀಮ್ ಅನ್ನು ಅಪ್ಪಿಕೊಳ್ಳುವುದು
ಬ್ಯಾಕ್ಪ್ರೆಶರ್ ಕೇವಲ ಕಾರ್ಯಕ್ಷಮತೆಯ ಆಪ್ಟಿಮೈಸೇಶನ್ ಅಲ್ಲ; ಇದು ದೊಡ್ಡ ಅಥವಾ ಅನಿರೀಕ್ಷಿತ ಪ್ರಮಾಣದ ಡೇಟಾವನ್ನು ನಿರ್ವಹಿಸುವ ದೃಢವಾದ, ಸ್ಥಿರವಾದ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ನಿರ್ಮಿಸಲು ಒಂದು ಮೂಲಭೂತ ಅವಶ್ಯಕತೆಯಾಗಿದೆ. ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ನ ಅಸಿಂಕ್ ಇಟರೇಟರ್ಗಳು ಮತ್ತು for await...of ಸಿಂಟ್ಯಾಕ್ಸ್ ಈ ಶಕ್ತಿಯುತ ಪರಿಕಲ್ಪನೆಯನ್ನು ಪ್ರಜಾಪ್ರಭುತ್ವಗೊಳಿಸಿದೆ, ಅದನ್ನು ವಿಶೇಷ ಸ್ಟ್ರೀಮ್ ಲೈಬ್ರರಿಗಳ ವಲಯದಿಂದ ಮೂಲ ಭಾಷೆಗೆ ಸರಿಸಿದೆ.
ಈ ಪುಲ್-ಆಧಾರಿತ, ಘೋಷಣಾತ್ಮಕ ಮಾದರಿಯನ್ನು ಅಪ್ಪಿಕೊಳ್ಳುವ ಮೂಲಕ, ನೀವು:
- ಮೆಮೊರಿ ಕ್ರ್ಯಾಶ್ಗಳನ್ನು ತಡೆಯಿರಿ: ಡೇಟಾ ಗಾತ್ರವನ್ನು ಲೆಕ್ಕಿಸದೆ, ಸಣ್ಣ, ಸ್ಥಿರವಾದ ಮೆಮೊರಿ ಬಳಕೆಯನ್ನು ಹೊಂದಿರುವ ಕೋಡ್ ಬರೆಯಿರಿ.
- ಓದುವಿಕೆಯನ್ನು ಸುಧಾರಿಸಿ: ಓದಲು, ಸಂಯೋಜಿಸಲು ಮತ್ತು ತರ್ಕಿಸಲು ಸುಲಭವಾದ ಸಂಕೀರ್ಣ ಡೇಟಾ ಪೈಪ್ಲೈನ್ಗಳನ್ನು ರಚಿಸಿ.
- ಸ್ಥಿತಿಸ್ಥಾಪಕ ವ್ಯವಸ್ಥೆಗಳನ್ನು ನಿರ್ಮಿಸಿ: ಫೈಲ್ ಸಿಸ್ಟಮ್ಗಳು ಮತ್ತು ಡೇಟಾಬೇಸ್ಗಳಿಂದ ಹಿಡಿದು APIಗಳು ಮತ್ತು ರಿಯಲ್-ಟೈಮ್ ಫೀಡ್ಗಳವರೆಗೆ ವಿವಿಧ ಘಟಕಗಳ ನಡುವೆ ಫ್ಲೋ ಕಂಟ್ರೋಲ್ ಅನ್ನು ಆಕರ್ಷಕವಾಗಿ ನಿಭಾಯಿಸುವ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಅಭಿವೃದ್ಧಿಪಡಿಸಿ.
ಮುಂದಿನ ಬಾರಿ ನೀವು ಡೇಟಾ ಪ್ರವಾಹವನ್ನು ಎದುರಿಸಿದಾಗ, ಸಂಕೀರ್ಣ ಲೈಬ್ರರಿ ಅಥವಾ ಹ್ಯಾಕಿ ಪರಿಹಾರವನ್ನು ಆಶ್ರಯಿಸಬೇಡಿ. ಬದಲಿಗೆ, ಅಸಿಂಕ್ ಇಟರೇಬಲ್ಗಳ ವಿಷಯದಲ್ಲಿ ಯೋಚಿಸಿ. ಗ್ರಾಹಕನು ತನ್ನದೇ ಆದ ವೇಗದಲ್ಲಿ ಡೇಟಾವನ್ನು ಎಳೆಯಲು ಬಿಡುವ ಮೂಲಕ, ನೀವು ಬರೆಯುವ ಕೋಡ್ ಹೆಚ್ಚು ಸಮರ್ಥ ಮಾತ್ರವಲ್ಲದೆ ದೀರ್ಘಾವಧಿಯಲ್ಲಿ ಹೆಚ್ಚು ಸೊಗಸಾದ ಮತ್ತು ನಿರ್ವಹಿಸಬಲ್ಲದು ಆಗಿರುತ್ತದೆ.