Tutustu Web Streams API:in tehokkaaseen datankäsittelyyn JavaScriptissä. Opi luomaan, muuntamaan ja kuluttamaan striimejä suorituskyvyn ja muistinhallinnan parantamiseksi.
Web Streams API: Tehokkaat datankäsittelyputket JavaScriptissä
Web Streams API tarjoaa tehokkaan mekanismin striimattavan datan käsittelyyn JavaScriptissä, mahdollistaen tehokkaat ja responsiiviset verkkosovellukset. Sen sijaan, että koko datajoukko ladattaisiin muistiin kerralla, striimien avulla voit käsitellä dataa vaiheittain, mikä vähentää muistin kulutusta ja parantaa suorituskykyä. Tämä on erityisen hyödyllistä suurten tiedostojen, verkkopyyntöjen tai reaaliaikaisten datasyötteiden kanssa.
Mitä Web-striimit ovat?
Ytimessään Web Streams API tarjoaa kolme päätyyppiä striimejä:
- ReadableStream: Edustaa datan lähdettä, kuten tiedostoa, verkkoyhteyttä tai generoitua dataa.
- WritableStream: Edustaa datan kohdetta, kuten tiedostoa, verkkoyhteyttä tai tietokantaa.
- TransformStream: Edustaa muunnosputkea ReadableStreamin ja WritableStreamin välillä. Se voi muokata tai käsitellä dataa sen virratessa striimin läpi.
Nämä striimityypit toimivat yhdessä luoden tehokkaita datankäsittelyputkia. Data virtaa ReadableStreamista, valinnaisten TransformStreamien läpi ja lopulta WritableStreamiin.
Keskeiset käsitteet ja terminologia
- Palaset (Chunks): Dataa käsitellään erillisinä yksikköinä, joita kutsutaan palasiksi (chunks). Palanen voi olla mikä tahansa JavaScript-arvo, kuten merkkijono, numero tai objekti.
- Ohjaimet (Controllers): Jokaisella striimityypillä on vastaava ohjainobjekti, joka tarjoaa metodeja striimin hallintaan. Esimerkiksi ReadableStreamControllerin avulla voit jonottaa dataa striimiin, kun taas WritableStreamControllerin avulla voit käsitellä saapuvia palasia.
- Putket (Pipes): Striimejä voidaan yhdistää toisiinsa käyttämällä
pipeTo()
- japipeThrough()
-metodeja.pipeTo()
yhdistää ReadableStreamin WritableStreamiin, kun taaspipeThrough()
yhdistää ReadableStreamin TransformStreamiin ja sen jälkeen WritableStreamiin. - Vastapaine (Backpressure): Mekanismi, jonka avulla kuluttaja voi viestittää tuottajalle, ettei se ole valmis vastaanottamaan lisää dataa. Tämä estää kuluttajaa ylikuormittumasta ja varmistaa, että dataa käsitellään kestävällä nopeudella.
ReadableStreamin luominen
Voit luoda ReadableStreamin käyttämällä ReadableStream()
-konstruktoria. Konstruktori ottaa argumenttina objektin, joka voi määritellä useita metodeja striimin käyttäytymisen ohjaamiseksi. Tärkeimmät näistä ovat start()
-metodi, jota kutsutaan kun striimi luodaan, ja pull()
-metodi, jota kutsutaan kun striimi tarvitsee lisää dataa.
Tässä on esimerkki ReadableStreamin luomisesta, joka generoi numerosarjan:
const readableStream = new ReadableStream({
start(controller) {
let counter = 0;
function push() {
if (counter >= 10) {
controller.close();
return;
}
controller.enqueue(counter++);
setTimeout(push, 100);
}
push();
},
});
Tässä esimerkissä start()
-metodi alustaa laskurin ja määrittelee push()
-funktion, joka jonottaa numeron striimiin ja kutsuu sitten itseään uudelleen lyhyen viiveen jälkeen. controller.close()
-metodia kutsutaan, kun laskuri saavuttaa arvon 10, mikä viestii striimin päättymisestä.
ReadableStreamin kuluttaminen
Kuluttaaksesi dataa ReadableStreamista voit käyttää ReadableStreamDefaultReader
-oliota. Lukija tarjoaa metodeja palasten lukemiseen striimistä. Tärkein näistä on read()
-metodi, joka palauttaa promisen, joka ratkeaa objektilla, joka sisältää datapalaseen ja lipun, joka ilmaisee onko striimi päättynyt.
Tässä on esimerkki datan kuluttamisesta edellisessä esimerkissä luodusta ReadableStreamista:
const reader = readableStream.getReader();
async function read() {
const { done, value } = await reader.read();
if (done) {
console.log('Stream complete');
return;
}
console.log('Received:', value);
read();
}
read();
Tässä esimerkissä read()
-funktio lukee palasen striimistä, tulostaa sen konsoliin ja kutsuu sitten itseään uudelleen, kunnes striimi on päättynyt.
WritableStreamin luominen
Voit luoda WritableStreamin käyttämällä WritableStream()
-konstruktoria. Konstruktori ottaa argumenttina objektin, joka voi määritellä useita metodeja striimin käyttäytymisen ohjaamiseksi. Tärkeimmät näistä ovat write()
-metodi, jota kutsutaan kun datapalane on valmis kirjoitettavaksi, close()
-metodi, jota kutsutaan kun striimi suljetaan, ja abort()
-metodi, jota kutsutaan kun striimi keskeytetään.
Tässä on esimerkki WritableStreamin luomisesta, joka tulostaa jokaisen datapalaseen konsoliin:
const writableStream = new WritableStream({
write(chunk) {
console.log('Writing:', chunk);
return Promise.resolve(); // Indicate success
},
close() {
console.log('Stream closed');
},
abort(err) {
console.error('Stream aborted:', err);
},
});
Tässä esimerkissä write()
-metodi tulostaa palasen konsoliin ja palauttaa promisen, joka ratkeaa, kun palanen on onnistuneesti kirjoitettu. close()
- ja abort()
-metodit tulostavat viestejä konsoliin, kun striimi suljetaan tai keskeytetään.
WritableStreamiin kirjoittaminen
Kirjoittaaksesi dataa WritableStreamiin voit käyttää WritableStreamDefaultWriter
-oliota. Kirjoittaja tarjoaa metodeja palasten kirjoittamiseen striimiin. Tärkein näistä on write()
-metodi, joka ottaa datapalaseen argumenttina ja palauttaa promisen, joka ratkeaa, kun palanen on onnistuneesti kirjoitettu.
Tässä on esimerkki datan kirjoittamisesta edellisessä esimerkissä luotuun WritableStreamiin:
const writer = writableStream.getWriter();
async function writeData() {
await writer.write('Hello, world!');
await writer.close();
}
writeData();
Tässä esimerkissä writeData()
-funktio kirjoittaa merkkijonon "Hello, world!" striimiin ja sulkee sitten striimin.
TransformStreamin luominen
Voit luoda TransformStreamin käyttämällä TransformStream()
-konstruktoria. Konstruktori ottaa argumenttina objektin, joka voi määritellä useita metodeja striimin käyttäytymisen ohjaamiseksi. Tärkeimmät näistä ovat transform()
-metodi, jota kutsutaan kun datapalane on valmis muunnettavaksi, ja flush()
-metodi, jota kutsutaan kun striimi suljetaan.
Tässä on esimerkki TransformStreamin luomisesta, joka muuntaa jokaisen datapalaseen suuraakkosiksi:
const transformStream = new TransformStream({
transform(chunk, controller) {
controller.enqueue(chunk.toUpperCase());
},
flush(controller) {
// Optional: Perform any final operations when the stream is closing
},
});
Tässä esimerkissä transform()
-metodi muuntaa palasen suuraakkosiksi ja jonottaa sen ohjaimen jonoon. flush()
-metodia kutsutaan, kun striimi sulkeutuu, ja sitä voidaan käyttää viimeisten toimenpiteiden suorittamiseen.
TransformStreamien käyttäminen putkissa
TransformStreamit ovat hyödyllisimpiä, kun ne ketjutetaan yhteen datankäsittelyputkien luomiseksi. Voit käyttää pipeThrough()
-metodia yhdistääksesi ReadableStreamin TransformStreamiin ja sen jälkeen WritableStreamiin.
Tässä on esimerkki putken luomisesta, joka lukee dataa ReadableStreamista, muuntaa sen suuraakkosiksi TransformStreamin avulla ja kirjoittaa sen sitten WritableStreamiin:
const readableStream = new ReadableStream({
start(controller) {
controller.enqueue('hello');
controller.enqueue('world');
controller.close();
},
});
const transformStream = new TransformStream({
transform(chunk, controller) {
controller.enqueue(chunk.toUpperCase());
},
});
const writableStream = new WritableStream({
write(chunk) {
console.log('Writing:', chunk);
return Promise.resolve();
},
});
readableStream.pipeThrough(transformStream).pipeTo(writableStream);
Tässä esimerkissä pipeThrough()
-metodi yhdistää readableStream
-olion transformStream
-olioon, ja sitten pipeTo()
-metodi yhdistää transformStream
-olion writableStream
-olioon. Data virtaa ReadableStreamista, TransformStreamin läpi (jossa se muunnetaan suuraakkosiksi) ja sitten WritableStreamiin (jossa se tulostetaan konsoliin).
Vastapaine
Vastapaine on Web-striimien tärkeä mekanismi, joka estää nopeaa tuottajaa ylikuormittamasta hidasta kuluttajaa. Kun kuluttaja ei pysty pysymään datan tuotantonopeuden mukana, se voi viestittää tuottajalle hidastamisesta. Tämä saavutetaan striimin ohjaimen ja lukija/kirjoittaja-objektien avulla.
Kun ReadableStreamin sisäinen jono on täynnä, pull()
-metodia ei kutsuta ennen kuin jonossa on tilaa. Vastaavasti WritableStreamin write()
-metodi voi palauttaa promisen, joka ratkeaa vasta kun striimi on valmis vastaanottamaan lisää dataa.
Käsittelemällä vastapainetta oikein voit varmistaa, että datankäsittelyputkesi ovat vankkoja ja tehokkaita, jopa vaihtelevien datanopeuksien kanssa.
Käyttötapauksia ja esimerkkejä
1. Suurten tiedostojen käsittely
Web Streams API on ihanteellinen suurten tiedostojen käsittelyyn ilman, että niitä ladataan kokonaan muistiin. Voit lukea tiedostoa palasina, käsitellä jokaisen palasen ja kirjoittaa tulokset toiseen tiedostoon tai striimiin.
async function processFile(inputFile, outputFile) {
const readableStream = fs.createReadStream(inputFile).pipeThrough(new TextDecoderStream());
const writableStream = fs.createWriteStream(outputFile).pipeThrough(new TextEncoderStream());
const transformStream = new TransformStream({
transform(chunk, controller) {
// Esimerkki: Muunna jokainen rivi suuraakkosiksi
const lines = chunk.split('\n');
lines.forEach(line => controller.enqueue(line.toUpperCase() + '\n'));
}
});
await readableStream.pipeThrough(transformStream).pipeTo(writableStream);
console.log('File processing complete!');
}
// Esimerkkikäyttö (vaatii Node.js:n)
// const fs = require('fs');
// processFile('input.txt', 'output.txt');
2. Verkkopyyntöjen käsittely
Voit käyttää Web Streams API:a verkkopyynnöistä, kuten API-vastauksista tai palvelimen lähettämistä tapahtumista (server-sent events), vastaanotetun datan käsittelyyn. Tämä mahdollistaa datan käsittelyn aloittamisen heti sen saapuessa, sen sijaan että odottaisit koko vastauksen latautumista.
async function fetchAndProcessData(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) {
break;
}
const text = decoder.decode(value);
// Käsittele vastaanotettu data
console.log('Received:', text);
}
} catch (error) {
console.error('Error reading from stream:', error);
} finally {
reader.releaseLock();
}
}
// Esimerkkikäyttö
// fetchAndProcessData('https://example.com/api/data');
3. Reaaliaikaiset datasyötteet
Web-striimit soveltuvat myös reaaliaikaisten datasyötteiden, kuten osakekurssien tai anturilukemien, käsittelyyn. Voit yhdistää ReadableStreamin datalähteeseen ja käsitellä saapuvaa dataa sen saapuessa.
// Esimerkki: Reaaliaikaisen datasyötteen simulointi
const readableStream = new ReadableStream({
start(controller) {
let intervalId = setInterval(() => {
const data = Math.random(); // Simuloi anturilukemaa
controller.enqueue(`Data: ${data.toFixed(2)}`);
}, 1000);
this.cancel = () => {
clearInterval(intervalId);
controller.close();
};
},
cancel() {
this.cancel();
}
});
const reader = readableStream.getReader();
async function readStream() {
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
console.log('Stream closed.');
break;
}
console.log('Received:', value);
}
} catch (error) {
console.error('Error reading from stream:', error);
} finally {
reader.releaseLock();
}
}
readStream();
// Pysäytä striimi 10 sekunnin kuluttua
setTimeout(() => {readableStream.cancel()}, 10000);
Web Streams API:n käytön edut
- Parempi suorituskyky: Käsittele dataa vaiheittain, mikä vähentää muistin kulutusta ja parantaa reagoivuutta.
- Tehostettu muistinhallinta: Vältä kokonaisten datajoukkojen lataamista muistiin, mikä on erityisen hyödyllistä suurten tiedostojen tai verkkostriimien kanssa.
- Parempi käyttäjäkokemus: Aloita datan käsittely ja näyttäminen nopeammin, mikä tarjoaa interaktiivisemman ja reagoivamman käyttökokemuksen.
- Yksinkertaistettu datankäsittely: Luo modulaarisia ja uudelleenkäytettäviä datankäsittelyputkia TransformStreamien avulla.
- Vastapainetuki: Käsittele vaihtelevia datanopeuksia ja estä kuluttajia ylikuormittumasta.
Huomioitavaa ja parhaat käytännöt
- Virheidenkäsittely: Toteuta vankka virheidenkäsittely käsitelläksesi striimivirheet hallitusti ja estääksesi odottamattoman sovelluskäyttäytymisen.
- Resurssienhallinta: Vapauta resurssit asianmukaisesti, kun striimejä ei enää tarvita, muistivuotojen välttämiseksi. Käytä
reader.releaseLock()
ja varmista, että striimit suljetaan tai keskeytetään tarvittaessa. - Koodaus ja dekoodaus: Käytä
TextEncoderStream
- jaTextDecoderStream
-olioita tekstipohjaisen datan käsittelyyn varmistaaksesi oikean merkkikoodauksen. - Selainyhteensopivuus: Tarkista selainyhteensopivuus ennen Web Streams API:n käyttöä ja harkitse polyfillien käyttöä vanhemmille selaimille.
- Testaus: Testaa datankäsittelyputkesi perusteellisesti varmistaaksesi, että ne toimivat oikein erilaisissa olosuhteissa.
Yhteenveto
Web Streams API tarjoaa tehokkaan ja vaikuttavan tavan käsitellä striimattavaa dataa JavaScriptissä. Ymmärtämällä ydinkäsitteet ja hyödyntämällä eri striimityyppejä voit luoda vankkoja ja responsiivisia verkkosovelluksia, jotka käsittelevät suuria tiedostoja, verkkopyyntöjä ja reaaliaikaisia datasyötteitä vaivattomasti. Vastapaineen toteuttaminen ja parhaiden käytäntöjen noudattaminen virheidenkäsittelyssä ja resurssienhallinnassa varmistavat, että datankäsittelyputkesi ovat luotettavia ja suorituskykyisiä. Verkkosovellusten kehittyessä ja käsitellessä yhä monimutkaisempaa dataa Web Streams API:sta tulee olennainen työkalu kehittäjille maailmanlaajuisesti.