BemÀstra asynkron iteration i JavaScript med 'for await...of'-loopen och anpassade asynkrona iteratorhjÀlpare. FörbÀttra strömbehandling och datahantering med praktiska exempel.
JavaScript asynkron iteratorhjĂ€lpare: For Each â Iteration för strömbehandling
Asynkron programmering Àr en hörnsten i modern JavaScript-utveckling, vilket gör det möjligt för applikationer att hantera tidskrÀvande operationer utan att blockera huvudtrÄden. Asynkrona iteratorer, som introducerades i ECMAScript 2018, erbjuder en kraftfull mekanism för att bearbeta dataströmmar asynkront. Detta blogginlÀgg fördjupar sig i konceptet med asynkrona iteratorer och visar hur man implementerar en asynkron 'for each'-hjÀlpfunktion för att effektivisera strömbehandling.
FörstÄ asynkrona iteratorer
En asynkron iterator Àr ett objekt som följer AsyncIterator-grÀnssnittet. Det definierar en next()-metod som returnerar ett promise, vilket löses till ett objekt med tvÄ egenskaper:
value: NÀsta vÀrde i sekvensen.done: En boolean som indikerar om iteratorn har slutförts.
Asynkrona iteratorer anvÀnds vanligtvis för att konsumera data frÄn asynkrona kÀllor som nÀtverksströmmar, filsystem eller databaser. for await...of-loopen erbjuder en bekvÀm syntax för att iterera över asynkrona itererbara objekt.
Exempel: LÀsa frÄn en fil asynkront
TÀnk dig ett scenario dÀr du behöver lÀsa en stor fil rad för rad utan att blockera huvudtrÄden. Du kan uppnÄ detta med hjÀlp av en asynkron iterator:
const fs = require('fs');
const readline = require('readline');
async function* readFileLines(filePath) {
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
for await (const line of rl) {
yield line;
}
}
async function processFile(filePath) {
for await (const line of readFileLines(filePath)) {
console.log(`Rad: ${line}`);
}
}
// ExempelanvÀndning
processFile('sökvÀg/till/din/fil.txt');
I detta exempel Àr readFileLines en asynkron generatorfunktion som yieldar varje rad i filen nÀr den lÀses. Funktionen processFile itererar sedan över raderna med hjÀlp av for await...of och bearbetar varje rad asynkront.
Implementera en asynkron 'For Each'-hjÀlpfunktion
Ăven om for await...of-loopen Ă€r anvĂ€ndbar, kan den bli mĂ„ngordig nĂ€r du behöver utföra komplexa operationer pĂ„ varje element i strömmen. En asynkron 'for each'-hjĂ€lpfunktion kan förenkla denna process genom att kapsla in iterationslogiken.
GrundlÀggande implementation
HÀr Àr en grundlÀggande implementation av en asynkron 'for each'-funktion:
async function asyncForEach(iterable, callback) {
for await (const item of iterable) {
await callback(item);
}
}
Denna funktion tar ett asynkront itererbart objekt och en callback-funktion som argument. Den itererar över det itererbara objektet med for await...of och anropar callback-funktionen för varje element. Callback-funktionen bör ocksÄ vara asynkron om du vill invÀnta dess slutförande innan du gÄr vidare till nÀsta element.
Exempel: Bearbeta data frÄn ett API
Anta att du hÀmtar data frÄn ett API som returnerar en ström av objekt. Du kan anvÀnda den asynkrona 'for each'-hjÀlpfunktionen för att bearbeta varje objekt nÀr det anlÀnder:
async function* fetchDataStream(url) {
const response = await fetch(url);
const reader = response.body.getReader();
const decoder = new TextDecoder();
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
return;
}
// Förutsatt att API:et returnerar JSON-stycken
const chunk = decoder.decode(value);
const items = JSON.parse(`[${chunk.replace(/\}\{/g, '},{')}]`); //Dela upp stycken i en giltig JSON-array
for(const item of items){
yield item;
}
}
} finally {
reader.releaseLock();
}
}
async function processItem(item) {
// Simulera en asynkron operation
await new Promise(resolve => setTimeout(resolve, 100));
console.log(`Bearbetar objekt: ${JSON.stringify(item)}`);
}
async function main() {
const apiUrl = 'https://api.example.com/data'; // ErsÀtt med din API-slutpunkt
await asyncForEach(fetchDataStream(apiUrl), processItem);
console.log('FĂ€rdig med databearbetning.');
}
// ExempelanvÀndning
main();
I detta exempel hÀmtar fetchDataStream data frÄn API:et och yieldar varje objekt nÀr det tas emot. Funktionen processItem simulerar en asynkron operation pÄ varje objekt. HjÀlpfunktionen asyncForEach förenklar sedan iterations- och bearbetningslogiken.
FörbÀttringar och övervÀganden
Felhantering
Det Àr avgörande att hantera fel som kan uppstÄ under asynkron iteration. Du kan omsluta callback-funktionen i ett try...catch-block för att fÄnga och hantera undantag:
async function asyncForEach(iterable, callback) {
for await (const item of iterable) {
try {
await callback(item);
} catch (error) {
console.error(`Fel vid bearbetning av objekt: ${item}`, error);
// Du kan vÀlja att kasta om felet eller fortsÀtta bearbetningen
}
}
}
Samtidighetskontroll
Som standard bearbetar den asynkrona 'for each'-hjÀlpfunktionen objekt sekventiellt. Om du behöver bearbeta objekt samtidigt kan du anvÀnda en Promise-pool för att begrÀnsa antalet samtidiga operationer:
async function asyncForEachConcurrent(iterable, callback, concurrency) {
const executing = [];
for await (const item of iterable) {
const p = callback(item).then(() => executing.splice(executing.indexOf(p), 1));
executing.push(p);
if (executing.length >= concurrency) {
await Promise.race(executing);
}
}
await Promise.all(executing);
}
async function processItem(item) {
// Simulera en asynkron operation
await new Promise(resolve => setTimeout(resolve, 100));
console.log(`Bearbetar objekt: ${JSON.stringify(item)}`);
}
async function main() {
const apiUrl = 'https://api.example.com/data'; // ErsÀtt med din API-slutpunkt
await asyncForEachConcurrent(fetchDataStream(apiUrl), processItem, 5); // Samtidighet pÄ 5
console.log('FĂ€rdig med databearbetning.');
}
I detta exempel begrÀnsar asyncForEachConcurrent antalet samtidiga callback-körningar till den angivna samtidighetsnivÄn. Detta kan förbÀttra prestandan vid hantering av stora dataströmmar.
Avbrytande
I vissa fall kan du behöva avbryta iterationsprocessen i förtid. Du kan uppnÄ detta genom att anvÀnda en AbortController:
async function asyncForEach(iterable, callback, signal) {
for await (const item of iterable) {
if (signal && signal.aborted) {
console.log('Iteration avbruten.');
return;
}
await callback(item);
}
}
async function main() {
const controller = new AbortController();
const signal = controller.signal;
setTimeout(() => {
controller.abort(); // Avbryt efter 2 sekunder
}, 2000);
const apiUrl = 'https://api.example.com/data'; // ErsÀtt med din API-slutpunkt
await asyncForEach(fetchDataStream(apiUrl), processItem, signal);
console.log('FĂ€rdig med databearbetning.');
}
I detta exempel kontrollerar asyncForEach-funktionen egenskapen signal.aborted före varje iteration. Om signalen har avbrutits stoppas iterationen.
Verkliga tillÀmpningar
Asynkrona iteratorer och den asynkrona 'for each'-hjÀlpfunktionen kan tillÀmpas pÄ ett brett spektrum av verkliga scenarier:
- Databehandlingspipelines: Bearbetning av stora datamÀngder frÄn databaser eller filsystem.
- Realtidsdataströmmar: Hantering av data frÄn web sockets, meddelandeköer eller sensornÀtverk.
- API-konsumtion: HÀmta och bearbeta data frÄn API:er som returnerar strömmar av objekt.
- Bild- och videobearbetning: Bearbeta stora mediefiler i bitar.
- Logganalys: Analysera stora loggfiler rad för rad.
Exempel - Internationell aktiedata: TÀnk dig en applikation som hÀmtar aktiekurser i realtid frÄn olika internationella börser. En asynkron iterator kan anvÀndas för att strömma datan, och en asynkron 'for each' kan bearbeta varje kurs och uppdatera anvÀndargrÀnssnittet med de senaste priserna. Detta kan anvÀndas för att visa aktuella aktiekurser för företag som:
- Tencent (Kina): HÀmta aktiedata för ett stort internationellt teknikföretag
- Tata Consultancy Services (Indien): Visa aktieuppdateringar frÄn ett ledande IT-tjÀnsteföretag
- Samsung Electronics (Sydkorea): Visa aktiekurser frÄn en global elektroniktillverkare
- Toyota Motor Corporation (Japan): Ăvervaka aktiepriser för en internationell biltillverkare
Sammanfattning
Asynkrona iteratorer och den asynkrona 'for each'-hjÀlpfunktionen erbjuder ett kraftfullt och elegant sÀtt att bearbeta dataströmmar asynkront i JavaScript. Genom att kapsla in iterationslogiken kan du förenkla din kod, förbÀttra lÀsbarheten och öka prestandan i dina applikationer. Genom att hantera fel, kontrollera samtidighet och möjliggöra avbrytande kan du skapa robusta och skalbara asynkrona databehandlingspipelines.