Asynkrona iteratorer och for-await-of över strömmar
Konsumera Readable-strömmar smidigt med asynkron iteration och konvertera mellan strömmar och asynkrona generatorer.
Asynkrona iteratorer och for-await-of över strömmar är en gratis lektion i Bootcamp i backendutveckling med Node.js på CoddyKit. Detta är lektion 4 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Bootcamp i backendutveckling med Node.js, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Bootcamp i backendutveckling med Node.js innehåller totalt 4 lektioner.
Varför asynkron iteration över strömmar
Node.js Readable-strömmar skickar ut delar över tid. Det klassiska sättet att konsumera dem är att koppla in händelselyssnare för 'data', 'end' och 'error'. Det fungerar, men sprider logiken över callback-funktioner och gör pausning, återupptagning och felhantering omständliga.
Sedan Node 10 är alla Readable-strömmar asynkront itererbara. Det betyder att du kan konsumera dem med en vanlig for await...of-loop:
- Sekventiell bearbetning av delar, skriven uppifrån och ned
- Automatisk backpressure – loopen hämtar en del i taget
- try/catch fungerar för strömfel
Den här lektionen visar hur du på ett smidigt sätt kan konsumera, transformera och producera strömmar med asynkron iteration.
Grunderna i for-await-of
En for await...of-loop inväntar varje värde som produceras av en asynkron iterator innan den går vidare till nästa. När den används med en Readable-ström ger varje iteration en del (en Buffer som standard, eller en sträng om strömmens teckenkodning har angetts).
Här läser vi den här skriptfilens egen källkod rad för rad. Loopkroppen körs en gång per del och loopen avslutas naturligt när strömmen är klar:
import { createReadStream } from 'node:fs';
import { fileURLToPath } from 'node:url';
const self = fileURLToPath(import.meta.url);
const stream = createReadStream(self, { encoding: 'utf8' });
let chunks = 0;
let bytes = 0;
for await (const chunk of stream) {
chunks++;
bytes += chunk.length;
}
console.log('chunks:', chunks, 'chars:', bytes);Inbyggd backpressure
Den största fördelen med for await...of är automatisk backpressure. Loopen begär nästa del först när den föregående iterationens kropp har inväntat klart. Om din loopkropp utför långsamt asynkront arbete (en databasskrivning eller ett HTTP-anrop) pausas strömmen tills du är redo.
Du får detta automatiskt – ingen manuell dans med stream.pause() och stream.resume(). Det simulerade exemplet bearbetar varje del med en fördröjning, så strömmen väntar mellan iterationerna:
import { Readable } from 'node:stream';
import { setTimeout as sleep } from 'node:timers/promises';
const source = Readable.from(['a', 'b', 'c', 'd']);
for await (const item of source) {
// Slow consumer: stream is paused while we await
await sleep(50);
console.log('processed', item);
}
console.log('done');Readable.from(): från itererbart objekt till ström
Readable.from() omvandlar alla itererbara eller asynkront itererbara objekt (arrayer, generatorer och asynkrona generatorer) till en riktig Readable-ström. Det är länken från vanliga data eller generatorkod till strömekosystemet (pipes, HTTP-svar och filskrivningar).
Du kan skicka resultatet från en generatorfunktion till den för att producera värden stegvis:
import { Readable } from 'node:stream';
function* counter(n) {
for (let i = 1; i <= n; i++) {
yield `line ${i}\n`;
}
}
const stream = Readable.from(counter(3));
for await (const chunk of stream) {
process.stdout.write(chunk);
}Asynkrona generatorer som strömkällor
Readable.from() accepterar även en asynkron generator. Det låter dig producera strömdata från asynkrona källor – sidindelade API:er, köade jobb eller tidsstyrda händelser – samtidigt som koden förblir tydligt sekventiell.
Den asynkrona generatorn nedan ger ett objekt med korta intervall, och Readable.from() exponerar det som en standardström som alla Node-konsumenter kan läsa:
import { Readable } from 'node:stream';
import { setTimeout as sleep } from 'node:timers/promises';
async function* fetchPages() {
for (let page = 1; page <= 3; page++) {
await sleep(20); // simulate async fetch
yield { page, items: page * 2 };
}
}
const stream = Readable.from(fetchPages(), { objectMode: true });
for await (const record of stream) {
console.log('got page', record.page, 'items', record.items);
}Strömmar i objektläge
Som standard innehåller strömmar delar som är Buffer-objekt eller strängar. När du anger objectMode: true kan varje del vara ett godtyckligt JavaScript-värde – ett objekt, ett tal eller en array. Detta är viktigt när du itererar asynkront över strukturerade poster i stället för råa byte.
Readable.from() aktiverar automatiskt objektläge när källan ger värden som inte är strängar eller Buffer-objekt, men genom att ange det uttryckligen blir avsikten tydlig. Asynkron iteration ger sedan dina objekt direkt:
import { Readable } from 'node:stream';
const users = [
{ id: 1, name: 'Ada' },
{ id: 2, name: 'Linus' },
{ id: 3, name: 'Grace' },
];
const stream = Readable.from(users, { objectMode: true });
for await (const user of stream) {
console.log(`#${user.id} -> ${user.name}`);
}Transformering med asynkrona generatorer
Du behöver inte alltid en Transform-strömklass för att mappa eller filtrera strömdata. Du kan omsluta källströmmen i en asynkron generator som själv använder for await...of och ger transformerade värden. Det går naturligt att kombinera och liknar synkron kod.
Här tolkar en generator rader i NDJSON-format till objekt och filtrerar dem, samtidigt som backpressure bevaras:
import { Readable } from 'node:stream';
async function* onlyActive(source) {
for await (const record of source) {
if (record.active) {
yield record.name;
}
}
}
const input = Readable.from([
{ name: 'job-a', active: true },
{ name: 'job-b', active: false },
{ name: 'job-c', active: true },
], { objectMode: true });
for await (const name of onlyActive(input)) {
console.log('active:', name);
}Felhantering med try/catch
En stor ergonomisk fördel är att strömfel blir kastade undantag inuti loopen, så att vanlig try/catch kan hantera dem. Vid händelsebaserad konsumtion behövde du lyssna efter 'error' separat och riskerade ohanterade avvisade promises.
Om strömmen skickar ut 'error' mitt under iterationen avvisas det inväntade anropet till next() och kontrollen hoppar till catch:
import { Readable } from 'node:stream';
async function* faulty() {
yield 1;
yield 2;
throw new Error('source blew up');
}
const stream = Readable.from(faulty(), { objectMode: true });
try {
for await (const n of stream) {
console.log('value', n);
}
} catch (err) {
console.error('caught:', err.message);
}Ett tidigt break förstör strömmen
När du lämnar en for await...of-loop med break, return eller throw anropar Node iteratorns metod return(), som förstör den underliggande strömmen och frigör dess resurser (filbeskrivare och sockets). Detta är en viktig garanti för städning.
Om du alltså slutar efter den första träffen läcker inte det öppna filhandtaget – strömmen stängs automatiskt ned:
import { Readable } from 'node:stream';
const stream = Readable.from(['alpha', 'beta', 'gamma', 'delta']);
for await (const word of stream) {
console.log('checking', word);
if (word === 'beta') {
console.log('found it, stopping');
break; // stream is destroyed on break
}
}
console.log('destroyed?', stream.destroyed);Konsumera kroppen i en HTTP-begäran
I en Node-backend är en inkommande HTTP-begäran (req) en Readable-ström. Asynkron iteration är ett tydligt sätt att samla in eller bearbeta begärans kropp utan att manuellt buffra allt.
Det här mönstret samlar delar och tolkar dem sedan som JSON. Eftersom det är serverkod (den binder en port och behöver en HTTP-klient) är den illustrativ snarare än körbar direkt här:
import { createServer } from 'node:http';
const server = createServer(async (req, res) => {
try {
const chunks = [];
for await (const chunk of req) {
chunks.push(chunk);
}
const body = Buffer.concat(chunks).toString('utf8');
const data = JSON.parse(body || '{}');
res.end(JSON.stringify({ received: data }));
} catch (err) {
res.statusCode = 400;
res.end('bad request');
}
});
server.listen(3000);Iteratorhjälpare och stream.compose()
Moderna Node har hjälpfunktioner av högre ordning direkt på Readable-strömmar, som internt konsumerar den asynkrona iteratorn: stream.map(), stream.filter(), stream.toArray(), stream.take() med flera. De bevarar backpressure och låter dig slippa skriva loopar manuellt.
Du kan kedja dem på ett smidigt sätt. toArray() tömmer strömmen och returnerar en promise som löser sig till en vanlig array:
.map(fn)– transformera varje del (stöder asynkron fn och samtidighet).filter(fn)– behåll delar som matchar.toArray()– samla allt i en array
import { Readable } from 'node:stream';
const result = await Readable.from([1, 2, 3, 4, 5])
.filter((n) => n % 2 === 1)
.map((n) => n * 10)
.toArray();
console.log(result); // [10, 30, 50]Snabb kontroll
Du konsumerar en Readable-filström med for await...of och lämnar loopen med break efter att ha hittat den första matchande raden. Vad händer med den underliggande filresursen?
Sammanfattning
Du har lärt dig att konsumera och producera Node-strömmar med asynkron iteration:
- for await...of läser en Readable en del i taget med inbyggd backpressure och felhantering med try/catch.
- Readable.from() omvandlar arrayer, generatorer och asynkrona generatorer till strömmar. Använd
objectModeför strukturerade poster. - Omslut en källa i en asynkron generator för att mappa eller filtrera och samtidigt bevara backpressure – ingen Transform-klass behövs.
- break/return/throw förstör strömmen och frigör resurser automatiskt.
- Hjälpare som
.map(),.filter()och.toArray()erbjuder ett smidigt alternativ till manuella loopar.
Använd asynkron iteration när du vill ha sekventiell, lättläst och läckagesäker konsumtion av strömmar i din backend.
Lär dig JavaScript med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 22
- Lektioner
- 92
Vanliga frågor
Är lektionen ”Asynkrona iteratorer och for-await-of över strömmar” gratis?
Ja – hela texten till ”Asynkrona iteratorer och for-await-of över strömmar” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Bootcamp i backendutveckling med Node.js, kan Ni uppgradera till CoddyKit PRO. Kursen i Bootcamp i backendutveckling med Node.js innehåller totalt 4 lektioner.
Vad lär jag mig i ”Asynkrona iteratorer och for-await-of över strömmar”?
Konsumera Readable-strömmar smidigt med asynkron iteration och konvertera mellan strömmar och asynkrona generatorer. Ni övar på Bootcamp i backendutveckling med Node.js med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Bootcamp i backendutveckling med Node.js?
Du behöver inga förkunskaper. Utbildningen i Bootcamp i backendutveckling med Node.js på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 4 av 4.
Hur lång tid tar lektionen ”Asynkrona iteratorer och for-await-of över strömmar”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Bootcamp i backendutveckling med Node.js-lektionen?
Ja. Varje Bootcamp i backendutveckling med Node.js-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Readable-, Writable-, Duplex- och Transform-strömmars interna funktion
- Implementera anpassade Transform-strömmar med _transform och _flush
- Backpressure, pipe() och verktyget pipeline()
- Asynkrona iteratorer och for-await-of över strömmar