జావాస్క్రిప్ట్ అసైంక్ ఇటరేటర్ హెల్పర్ 'స్కాన్' యొక్క లోతైన పరిశీలన, దాని కార్యాచరణ, వినియోగ సందర్భాలు మరియు అసynchronous సంచిత ప్రాసెసింగ్ కోసం ప్రయోజనాలు.
JavaScript Async Iterator Helper: Scan - Async Accumulative Processing
Asynchronous ప్రోగ్రామింగ్ ఆధునిక జావాస్క్రిప్ట్ అభివృద్ధిలో ఒక మూలస్తంభం, ముఖ్యంగా నెట్వర్క్ అభ్యర్థనలు లేదా ఫైల్ సిస్టమ్ పరస్పర చర్యలు వంటి I/O- బౌండ్ కార్యకలాపాలను వ్యవహరించేటప్పుడు. ES2018 లో ప్రవేశపెట్టిన అసైంక్ ఇటరేటర్లు, అసynchronous డేటా ప్రవాహాలను నిర్వహించడానికి శక్తివంతమైన యంత్రాంగాన్ని అందిస్తాయి. RxJS వంటి లైబ్రరీలలో తరచుగా కనిపించే మరియు ఒక స్వతంత్ర యుటిలిటీగా మరింత అందుబాటులో ఉన్న `scan` సహాయకుడు, ఈ అసynchronous డేటా ప్రవాహాలను ప్రాసెస్ చేయడానికి మరింత సామర్థ్యాన్ని అన్లాక్ చేస్తుంది.
Understanding Async Iterators
`scan` లోకి ప్రవేశించే ముందు, అసైంక్ ఇటరేటర్లు ఏమిటో గుర్తు చేసుకుందాం. ఒక అసైంక్ ఇటరేటర్ అనేది అసైంక్ ఇటరేటర్ ప్రోటోకాల్కు అనుగుణంగా ఉండే ఒక వస్తువు. ఈ ప్రోటోకాల్ `next()` పద్ధతిని నిర్వచిస్తుంది, అది రెండు లక్షణాలతో ఒక వస్తువుకు పరిష్కరించే ఒక వాగ్దానాన్ని అందిస్తుంది: `value` (క్రమంలో తదుపరి విలువ) మరియు `done` (ఇటరేటర్ పూర్తయిందో లేదో సూచించే బూలియన్). డేటా కాలక్రమేణా వచ్చినప్పుడు లేదా డేటాను తిరిగి పొందడానికి అసynchronous కార్యకలాపాలు అవసరమైనప్పుడు అసైంక్ ఇటరేటర్లు ప్రత్యేకంగా ఉపయోగపడతాయి.
ఇక్కడ అసైంక్ ఇటరేటర్ యొక్క ప్రాథమిక ఉదాహరణ ఉంది:
async function* generateNumbers() {
yield 1;
yield 2;
yield 3;
}
async function main() {
const iterator = generateNumbers();
let result = await iterator.next();
console.log(result); // { value: 1, done: false }
result = await iterator.next();
console.log(result); // { value: 2, done: false }
result = await iterator.next();
console.log(result); // { value: 3, done: false }
result = await iterator.next();
console.log(result); // { value: undefined, done: true }
}
main();
Introducing the `scan` Helper
`scan` సహాయకుడు (దీనిని `accumulate` లేదా `reduce` అని కూడా అంటారు) ప్రతి విలువకు ఒక అక్యుములేటర్ ఫంక్షన్ను వర్తింపజేయడం మరియు సంచిత ఫలితాన్ని విడుదల చేయడం ద్వారా అసైంక్ ఇటరేటర్ను రూపాంతరం చేస్తుంది. ఇది శ్రేణులపై `reduce` పద్ధతికి సమానంగా ఉంటుంది, కానీ అసynchronousగా మరియు ఇటరేటర్లపై పనిచేస్తుంది.
సారాంశంలో, `scan` ఒక అసైంక్ ఇటరేటర్, ఒక అక్యుములేటర్ ఫంక్షన్ మరియు ఒక ఐచ్ఛిక ప్రారంభ విలువను తీసుకుంటుంది. సోర్స్ ఇటరేటర్ ద్వారా విడుదల చేయబడిన ప్రతి విలువ కోసం, అక్యుములేటర్ ఫంక్షన్ మునుపటి సంచిత విలువతో (లేదా ఇది మొదటి పునరావృతం అయితే ప్రారంభ విలువ) మరియు ఇటరేటర్ నుండి ప్రస్తుత విలువతో పిలువబడుతుంది. అక్యుములేటర్ ఫంక్షన్ యొక్క ఫలితం తదుపరి సంచిత విలువ అవుతుంది, ఇది ఫలితంగా వచ్చే అసైంక్ ఇటరేటర్ ద్వారా విడుదల చేయబడుతుంది.
Syntax and Parameters
`scan` ఉపయోగించడానికి సాధారణ సింటాక్స్ క్రింది విధంగా ఉంది:
async function* scan(sourceIterator, accumulator, initialValue) {
let accumulatedValue = initialValue;
for await (const value of sourceIterator) {
accumulatedValue = accumulator(accumulatedValue, value);
yield accumulatedValue;
}
}
- `sourceIterator`: రూపాంతరం చెందవలసిన అసైంక్ ఇటరేటర్.
- `accumulator`: రెండు ఆర్గ్యుమెంట్లను తీసుకునే ఒక ఫంక్షన్: మునుపటి సంచిత విలువ మరియు ఇటరేటర్ నుండి ప్రస్తుత విలువ. ఇది కొత్త సంచిత విలువను తిరిగి ఇవ్వాలి.
- `initialValue` (ఐచ్ఛికం): అక్యుములేటర్ కోసం ప్రారంభ విలువ. అందించకపోతే, సోర్స్ ఇటరేటర్ నుండి మొదటి విలువ ప్రారంభ విలువగా ఉపయోగించబడుతుంది మరియు అక్యుములేటర్ ఫంక్షన్ రెండవ విలువతో ప్రారంభమవుతుంది.
Use Cases and Examples
`scan` సహాయకుడు చాలా బహుముఖమైనది మరియు అసynchronous డేటా ప్రవాహాలను కలిగి ఉన్న విస్తృత శ్రేణి సందర్భాలలో ఉపయోగించవచ్చు. ఇక్కడ కొన్ని ఉదాహరణలు ఉన్నాయి:
1. Calculating a Running Total
మీకు లావాదేవీల మొత్తాలను విడుదల చేసే అసైంక్ ఇటరేటర్ ఉందని ఊహించుకోండి. ఈ లావాదేవీల యొక్క నడుస్తున్న మొత్తాన్ని లెక్కించడానికి మీరు `scan` ఉపయోగించవచ్చు.
async function* generateTransactions() {
yield 10;
yield 20;
yield 30;
}
async function main() {
const transactions = generateTransactions();
const runningTotals = scan(transactions, (acc, value) => acc + value, 0);
for await (const total of runningTotals) {
console.log(total); // Output: 10, 30, 60
}
}
main();
ఈ ఉదాహరణలో, `accumulator` ఫంక్షన్ ప్రస్తుత లావాదేవీ మొత్తాన్ని మునుపటి మొత్తానికి కలుపుతుంది. 0 యొక్క `initialValue` నడుస్తున్న మొత్తం సున్నా వద్ద ప్రారంభమవుతుందని నిర్ధారిస్తుంది.
2. Accumulating Data into an Array
ఒక శ్రేణిలోకి అసైంక్ ఇటరేటర్ నుండి డేటాను సేకరించడానికి మీరు `scan` ఉపయోగించవచ్చు. కాలక్రమేణా డేటాను సేకరించడానికి మరియు బ్యాచ్లలో ప్రాసెస్ చేయడానికి ఇది ఉపయోగపడుతుంది.
async function* fetchData() {
yield { id: 1, name: 'Alice' };
yield { id: 2, name: 'Bob' };
yield { id: 3, name: 'Charlie' };
}
async function main() {
const dataStream = fetchData();
const accumulatedData = scan(dataStream, (acc, value) => [...acc, value], []);
for await (const data of accumulatedData) {
console.log(data); // Output: [{id: 1, name: 'Alice'}], [{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}], [{id: 1, name: 'Alice'}, {id: 2, name: 'Bob'}, {id: 3, name: 'Charlie'}]
}
}
main();
ఇక్కడ, `accumulator` ఫంక్షన్ స్ప్రెడ్ ఆపరేటర్ (`...`) ను ఉపయోగించి అన్ని మునుపటి అంశాలు మరియు ప్రస్తుత విలువను కలిగి ఉన్న కొత్త శ్రేణిని సృష్టిస్తుంది. `initialValue` ఒక ఖాళీ శ్రేణి.
3. Implementing a Rate Limiter
మరింత క్లిష్టమైన వినియోగ సందర్భం రేట్ లిమిటర్ను అమలు చేయడం. ఒక నిర్దిష్ట సమయ విండోలో చేసిన అభ్యర్థనల సంఖ్యను ట్రాక్ చేయడానికి మరియు రేట్ పరిమితి మించితే తదుపరి అభ్యర్థనలను ఆలస్యం చేయడానికి మీరు `scan` ఉపయోగించవచ్చు.
async function* generateRequests() {
// Simulate incoming requests
yield Date.now();
await new Promise(resolve => setTimeout(resolve, 200));
yield Date.now();
await new Promise(resolve => setTimeout(resolve, 100));
yield Date.now();
}
async function main() {
const requests = generateRequests();
const rateLimitWindow = 1000; // 1 second
const maxRequestsPerWindow = 2;
async function* rateLimitedRequests(source, window, maxRequests) {
let queue = [];
for await (const requestTime of source) {
queue.push(requestTime);
queue = queue.filter(t => requestTime - t < window);
if (queue.length > maxRequests) {
const earliestRequest = queue[0];
const delay = window - (requestTime - earliestRequest);
console.log(`Rate limit exceeded. Delaying for ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
yield requestTime;
}
}
const limited = rateLimitedRequests(requests, rateLimitWindow, maxRequestsPerWindow);
for await (const requestTime of limited) {
console.log(`Request processed at ${requestTime}`);
}
}
main();
ఈ ఉదాహరణ అభ్యర్థనల టైమ్స్టాంప్ల క్యూను నిర్వహించడానికి అంతర్గతంగా `scan` ను ఉపయోగిస్తుంది (`rateLimitedRequests` ఫంక్షన్లో). రేట్ పరిమితి విండోలోని అభ్యర్థనల సంఖ్య గరిష్టంగా అనుమతించబడిన దానిని మించి ఉంటే ఇది తనిఖీ చేస్తుంది. అలా చేస్తే, అది అవసరమైన ఆలస్యాన్ని గణిస్తుంది మరియు అభ్యర్థనను ఇవ్వడానికి ముందు పాజ్ చేస్తుంది.
4. Building a Real-time Data Aggregator (Global Example)
వివిధ ఎక్స్ఛేంజీల నుండి నిజ-సమయ స్టాక్ ధరలను సేకరించాల్సిన గ్లోబల్ ఫైనాన్షియల్ అప్లికేషన్ను పరిశీలించండి. అసైంక్ ఇటరేటర్ న్యూయార్క్ స్టాక్ ఎక్స్ఛేంజ్ (NYSE), లండన్ స్టాక్ ఎక్స్ఛేంజ్ (LSE) మరియు టోక్యో స్టాక్ ఎక్స్ఛేంజ్ (TSE) వంటి ఎక్స్ఛేంజీల నుండి ధర నవీకరణలను స్ట్రీమ్ చేయవచ్చు. అన్ని ఎక్స్ఛేంజీలలో ఒక నిర్దిష్ట స్టాక్ కోసం నడుస్తున్న సగటు లేదా అధిక/తక్కువ ధరను నిర్వహించడానికి `scan` ఉపయోగించవచ్చు.
// Simulate streaming stock prices from different exchanges
async function* generateStockPrices() {
yield { exchange: 'NYSE', symbol: 'AAPL', price: 170.50 };
yield { exchange: 'LSE', symbol: 'AAPL', price: 170.75 };
await new Promise(resolve => setTimeout(resolve, 50));
yield { exchange: 'TSE', symbol: 'AAPL', price: 170.60 };
}
async function main() {
const stockPrices = generateStockPrices();
// Use scan to calculate a running average price
const runningAverages = scan(
stockPrices,
(acc, priceUpdate) => {
const { total, count } = acc;
return { total: total + priceUpdate.price, count: count + 1 };
},
{ total: 0, count: 0 }
);
for await (const averageData of runningAverages) {
const averagePrice = averageData.total / averageData.count;
console.log(`Running average price: ${averagePrice.toFixed(2)}`);
}
}
main();
ఈ ఉదాహరణలో, `accumulator` ఫంక్షన్ ధరల యొక్క నడుస్తున్న మొత్తం మరియు స్వీకరించిన నవీకరణల సంఖ్యను గణిస్తుంది. చివరి సగటు ధర ఈ సంచిత విలువల నుండి లెక్కించబడుతుంది. ఇది వివిధ ప్రపంచ మార్కెట్లలో స్టాక్ ధర యొక్క నిజ-సమయ వీక్షణను అందిస్తుంది.
5. Analyzing Website Traffic Globally
ప్రపంచవ్యాప్తంగా ఉన్న సర్వర్ల నుండి వెబ్సైట్ సందర్శన డేటా యొక్క ప్రవాహాలను స్వీకరించే గ్లోబల్ వెబ్ అనలిటిక్స్ ప్లాట్ఫారమ్ను ఊహించుకోండి. ప్రతి డేటా పాయింట్ వెబ్సైట్ను సందర్శించే వినియోగదారుని సూచిస్తుంది. `scan` ను ఉపయోగించి, మేము నిజ సమయంలో దేశానికి పేజీ వీక్షణల ట్రెండ్ను విశ్లేషించవచ్చు. డేటా ఇలా కనిపిస్తుందని చెప్పండి: `{ country: "US", page: "homepage", timestamp: 1678886400 }`.
async function* generateWebsiteVisits() {
yield { country: 'US', page: 'homepage', timestamp: Date.now() };
yield { country: 'CA', page: 'product', timestamp: Date.now() };
yield { country: 'UK', page: 'blog', timestamp: Date.now() };
yield { country: 'US', page: 'product', timestamp: Date.now() };
}
async function main() {
const visitStream = generateWebsiteVisits();
const pageViewCounts = scan(
visitStream,
(acc, visit) => {
const { country } = visit;
const newAcc = { ...acc };
newAcc[country] = (newAcc[country] || 0) + 1;
return newAcc;
},
{}
);
for await (const counts of pageViewCounts) {
console.log('Page view counts by country:', counts);
}
}
main();
ఇక్కడ, `accumulator` ఫంక్షన్ ప్రతి దేశానికి ఒక కౌంటర్ను నవీకరిస్తుంది. కొత్త సందర్శన డేటా వచ్చినప్పుడు ప్రతి దేశానికి సంచిత పేజీ వీక్షణ గణనలను అవుట్పుట్ చూపిస్తుంది.
Benefits of Using `scan`
అసైynchronous డేటా ప్రవాహాలతో పనిచేసేటప్పుడు `scan` సహాయకుడు అనేక ప్రయోజనాలను అందిస్తుంది:
- Declarative Style: కోడ్ రీడబిలిటీ మరియు మెయింటెనెన్స్ను మెరుగుపరుస్తూ, ప్రకటన మరియు సంక్షిప్త మార్గంలో సంచిత ప్రాసెసింగ్ లాజిక్ను వ్యక్తపరచడానికి `scan` మిమ్మల్ని అనుమతిస్తుంది.
- Asynchronous Handling: ఇది అక్యుములేటర్ ఫంక్షన్లో అసynchronous కార్యకలాపాలను సజావుగా నిర్వహిస్తుంది, I/O- బౌండ్ టాస్క్లను కలిగి ఉన్న క్లిష్టమైన దృశ్యాలకు ఇది అనుకూలంగా ఉంటుంది.
- Real-time Processing: మార్పులు జరిగినప్పుడు వాటికి ప్రతిస్పందించడానికి మిమ్మల్ని అనుమతిస్తూ, డేటా ప్రవాహాల యొక్క నిజ-సమయ ప్రాసెసింగ్ను `scan` ప్రారంభిస్తుంది.
- Composability: క్లిష్టమైన డేటా ప్రాసెసింగ్ పైప్లైన్లను సృష్టించడానికి ఇతర అసైంక్ ఇటరేటర్ సహాయకులతో దీనిని సులభంగా కంపోజ్ చేయవచ్చు.
Implementing `scan` (If It's Not Available)
కొన్ని లైబ్రరీలు అంతర్నిర్మిత `scan` సహాయకుడిని అందిస్తున్నప్పటికీ, అవసరమైతే మీరు మీ స్వంతంగా సులభంగా అమలు చేయవచ్చు. ఇక్కడ ఒక సాధారణ అమలు ఉంది:
async function* scan(sourceIterator, accumulator, initialValue) {
let accumulatedValue = initialValue;
let first = true;
for await (const value of sourceIterator) {
if (first && initialValue === undefined) {
accumulatedValue = value;
first = false;
} else {
accumulatedValue = accumulator(accumulatedValue, value);
}
yield accumulatedValue;
}
}
ఈ అమలు సోర్స్ ఇటరేటర్పై పునరావృతమవుతుంది మరియు ప్రతి విలువకు అక్యుములేటర్ ఫంక్షన్ వర్తిస్తుంది, సంచిత ఫలితాన్ని ఇస్తుంది. సోర్స్ ఇటరేటర్ నుండి మొదటి విలువను ప్రారంభ విలువగా ఉపయోగించడం ద్వారా `initialValue` అందించబడని సందర్భాన్ని ఇది నిర్వహిస్తుంది.
Comparison with `reduce`
`scan` ను `reduce` నుండి వేరు చేయడం ముఖ్యం. రెండూ ఇటరేటర్లపై పనిచేస్తాయి మరియు ఒక అక్యుములేటర్ ఫంక్షన్ను ఉపయోగిస్తాయి, అవి వాటి ప్రవర్తన మరియు అవుట్పుట్లో భిన్నంగా ఉంటాయి.
- `scan` సంచితాన్ని నడుపుతున్న చరిత్రను అందిస్తూ, ప్రతి పునరావృతం కోసం సంచిత విలువను విడుదల చేస్తుంది.
- `reduce` ఇటరేటర్లోని అన్ని అంశాలను ప్రాసెస్ చేసిన తర్వాత చివరి సంచిత విలువను మాత్రమే విడుదల చేస్తుంది.
అందువల్ల, సంచితం యొక్క మధ్యంతర స్థితులను ట్రాక్ చేయవలసిన దృశ్యాలకు `scan` అనుకూలంగా ఉంటుంది, అయితే మీకు చివరి ఫలితం మాత్రమే అవసరమైనప్పుడు `reduce` సముచితం.
Error Handling
అసైynchronous ఇటరేటర్లు మరియు `scan` తో పనిచేసేటప్పుడు, లోపాలను దయతో నిర్వహించడం చాలా ముఖ్యం. పునరావృత ప్రక్రియలో లేదా అక్యుములేటర్ ఫంక్షన్లో లోపాలు సంభవించవచ్చు. ఈ లోపాలను పట్టుకోవడానికి మరియు నిర్వహించడానికి మీరు `try...catch` బ్లాక్లను ఉపయోగించవచ్చు.
async function* generatePotentiallyFailingData() {
yield 1;
yield 2;
throw new Error('Something went wrong!');
yield 3;
}
async function main() {
const dataStream = generatePotentiallyFailingData();
try {
const accumulatedData = scan(dataStream, (acc, value) => acc + value, 0);
for await (const data of accumulatedData) {
console.log(data);
}
} catch (error) {
console.error('An error occurred:', error);
}
}
main();
ఈ ఉదాహరణలో, `try...catch` బ్లాక్ `generatePotentiallyFailingData` ఇటరేటర్ ద్వారా విసిరిన లోపాన్ని పట్టుకుంటుంది. మీరు ఆపై లోపాన్ని తగిన విధంగా నిర్వహించవచ్చు, దానిని లాగింగ్ చేయడం లేదా ఆపరేషన్ను మళ్లీ ప్రయత్నించడం వంటివి.
Conclusion
జావాస్క్రిప్ట్ అసైంక్ ఇటరేటర్లపై అసynchronous సంచిత ప్రాసెసింగ్ను నిర్వహించడానికి `scan` సహాయకుడు ఒక శక్తివంతమైన సాధనం. ఇది ప్రకటన మరియు సంక్షిప్త పద్ధతిలో క్లిష్టమైన డేటా మార్పులను వ్యక్తపరచడానికి, అసynchronous కార్యకలాపాలను దయతో నిర్వహించడానికి మరియు నిజ సమయంలో డేటా ప్రవాహాలను ప్రాసెస్ చేయడానికి మిమ్మల్ని అనుమతిస్తుంది. దాని కార్యాచరణ మరియు వినియోగ సందర్భాలను అర్థం చేసుకోవడం ద్వారా, మరింత బలమైన మరియు సమర్థవంతమైన అసynchronous అనువర్తనాలను రూపొందించడానికి మీరు `scan` ను ఉపయోగించవచ్చు. మీరు నడుస్తున్న మొత్తం లెక్కించడం, శ్రేణుల్లోకి డేటాను సేకరించడం, రేట్ లిమిటర్లను అమలు చేయడం లేదా నిజ-సమయ డేటా అగ్రిగేటర్లను రూపొందించడం వంటివి చేస్తున్నా, `scan` మీ కోడ్ను సరళీకృతం చేస్తుంది మరియు దాని మొత్తం పనితీరును మెరుగుపరుస్తుంది. లోపాన్ని నిర్వహించడాన్ని గుర్తుంచుకోండి మరియు మీ అసynchronous డేటా ప్రవాహాలను ప్రాసెస్ చేసే సమయంలో మధ్యంతర సంచిత విలువలకు మీకు యాక్సెస్ అవసరమైనప్పుడు `reduce` పై `scan` ను ఎంచుకోండి. రియాక్టివ్ ప్రోగ్రామింగ్ నమూనాలలో `scan` యొక్క మీ అవగాహన మరియు ఆచరణాత్మక అనువర్తనాన్ని మరింత మెరుగుపరచడానికి RxJS వంటి లైబ్రరీలను అన్వేషించడం.