Hallitse asynkroninen iterointi JavaScriptissä 'for await...of' -silmukan ja mukautettujen asynkronisten iteraattoriapurien avulla. Paranna virtakäsittelyä ja datankäsittelyä käytännön esimerkein.
JavaScriptin asynkronisen iteraattorin apuri: For Each - virtakäsittelyn iterointi
Asynkroninen ohjelmointi on modernin JavaScript-kehityksen kulmakivi, joka mahdollistaa sovellusten käsitellä aikaa vieviä operaatioita estämättä pääsäiettä. Asynkroniset iteraattorit, jotka esiteltiin ECMAScript 2018:ssa, tarjoavat tehokkaan mekanismin datavirtojen asynkroniseen käsittelyyn. Tämä blogikirjoitus syventyy asynkronisten iteraattorien käsitteeseen ja näyttää, kuinka toteuttaa asynkroninen 'for each' -apurifunktio virtakäsittelyn tehostamiseksi.
Asynkronisten iteraattorien ymmärtäminen
Asynkroninen iteraattori on objekti, joka noudattaa AsyncIterator-rajapintaa. Se määrittelee next()-metodin, joka palauttaa lupauksen (promise), joka ratkeaa objektiksi, jolla on kaksi ominaisuutta:
value: Seuraava arvo sekvenssissä.done: Boolean-arvo, joka ilmaisee, onko iteraattori suoritettu loppuun.
Asynkronisia iteraattoreita käytetään yleisesti datan kuluttamiseen asynkronisista lähteistä, kuten verkkoyhteyksistä, tiedostojärjestelmistä tai tietokannoista. for await...of -silmukka tarjoaa kätevän syntaksin asynkronisten iteroitavien läpikäyntiin.
Esimerkki: Tiedoston asynkroninen lukeminen
Harkitse tilannetta, jossa sinun on luettava suuri tiedosto rivi riviltä estämättä pääsäiettä. Voit saavuttaa tämän käyttämällä asynkronista iteraattoria:
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(`Line: ${line}`);
}
}
// Esimerkkikäyttö
processFile('path/to/your/file.txt');
Tässä esimerkissä readFileLines on asynkroninen generaattorifunktio, joka tuottaa (yield) jokaisen tiedoston rivin sitä mukaa kun se luetaan. processFile-funktio sitten iteroi rivien yli käyttäen for await...of -silmukkaa, käsitellen jokaisen rivin asynkronisesti.
Asynkronisen 'For Each' -apurin toteuttaminen
Vaikka for await...of -silmukka on hyödyllinen, siitä voi tulla monisanainen, kun joudut suorittamaan monimutkaisia operaatioita jokaiselle virran elementille. Asynkroninen 'for each' -apurifunktio voi yksinkertaistaa tätä prosessia kapseloimalla iterointilogiikan.
Perustoteutus
Tässä on perusimplementaatio asynkronisesta 'for each' -funktiosta:
async function asyncForEach(iterable, callback) {
for await (const item of iterable) {
await callback(item);
}
}
Tämä funktio ottaa argumenteikseen asynkronisen iteroitavan ja takaisinkutsufunktion (callback). Se iteroi iteroitavan läpi käyttäen for await...of -silmukkaa ja kutsuu takaisinkutsufunktiota jokaiselle kohteelle. Myös takaisinkutsufunktion tulisi olla asynkroninen, jos haluat odottaa sen valmistumista ennen siirtymistä seuraavaan kohteeseen.
Esimerkki: Datan käsittely API:sta
Oletetaan, että haet dataa API:sta, joka palauttaa kohteita virtana. Voit käyttää asynkronista 'for each' -apuria käsitelläksesi jokaisen kohteen sitä mukaa kun ne saapuvat:
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;
}
// Oletetaan, että API palauttaa JSON-palasia
const chunk = decoder.decode(value);
const items = JSON.parse(`[${chunk.replace(/\}\{/g, '},{')}]`); //Jaa palaset kelvolliseksi JSON-taulukoksi
for(const item of items){
yield item;
}
}
} finally {
reader.releaseLock();
}
}
async function processItem(item) {
// Simuloi asynkronista operaatiota
await new Promise(resolve => setTimeout(resolve, 100));
console.log(`Processing item: ${JSON.stringify(item)}`);
}
async function main() {
const apiUrl = 'https://api.example.com/data'; // Korvaa omalla API-päätepisteelläsi
await asyncForEach(fetchDataStream(apiUrl), processItem);
console.log('Finished processing data.');
}
// Esimerkkikäyttö
main();
Tässä esimerkissä fetchDataStream hakee dataa API:sta ja tuottaa jokaisen kohteen sitä mukaa kun se vastaanotetaan. processItem-funktio simuloi asynkronista operaatiota jokaiselle kohteelle. asyncForEach-apuri yksinkertaistaa sitten iterointi- ja käsittelylogiikkaa.
Parannukset ja huomioon otettavat seikat
Virheenkäsittely
On tärkeää käsitellä virheet, jotka saattavat ilmetä asynkronisen iteroinnin aikana. Voit kääriä takaisinkutsufunktion try...catch-lohkoon napataksesi ja käsitelläksesi poikkeuksia:
async function asyncForEach(iterable, callback) {
for await (const item of iterable) {
try {
await callback(item);
} catch (error) {
console.error(`Error processing item: ${item}`, error);
// Voit valita heittää virheen uudelleen tai jatkaa käsittelyä
}
}
}
Rinnakkaisuuden hallinta
Oletusarvoisesti asynkroninen 'for each' -apuri käsittelee kohteet peräkkäin. Jos sinun täytyy käsitellä kohteita rinnakkain, voit käyttää Promise-poolia rajoittaaksesi rinnakkaisten operaatioiden määrää:
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) {
// Simuloi asynkronista operaatiota
await new Promise(resolve => setTimeout(resolve, 100));
console.log(`Processing item: ${JSON.stringify(item)}`);
}
async function main() {
const apiUrl = 'https://api.example.com/data'; // Korvaa omalla API-päätepisteelläsi
await asyncForEachConcurrent(fetchDataStream(apiUrl), processItem, 5); // Rinnakkaisuus 5
console.log('Finished processing data.');
}
Tässä esimerkissä asyncForEachConcurrent rajoittaa rinnakkaisten takaisinkutsujen suoritusten määrän määritettyyn rinnakkaisuustasoon. Tämä voi parantaa suorituskykyä käsiteltäessä suuria datavirtoja.
Peruuttaminen
Joissakin tapauksissa saatat joutua peruuttamaan iterointiprosessin ennenaikaisesti. Voit saavuttaa tämän käyttämällä AbortControlleria:
async function asyncForEach(iterable, callback, signal) {
for await (const item of iterable) {
if (signal && signal.aborted) {
console.log('Iteration aborted.');
return;
}
await callback(item);
}
}
async function main() {
const controller = new AbortController();
const signal = controller.signal;
setTimeout(() => {
controller.abort(); // Peruuta 2 sekunnin kuluttua
}, 2000);
const apiUrl = 'https://api.example.com/data'; // Korvaa omalla API-päätepisteelläsi
await asyncForEach(fetchDataStream(apiUrl), processItem, signal);
console.log('Finished processing data.');
}
Tässä esimerkissä asyncForEach-funktio tarkistaa signal.aborted-ominaisuuden ennen jokaista iteraatiota. Jos signaali on peruutettu, iterointi pysäytetään.
Sovellukset todellisessa maailmassa
Asynkronisia iteraattoreita ja asynkronista 'for each' -apuria voidaan soveltaa laajaan valikoimaan todellisen maailman tilanteita:
- Datan käsittelyputket: Suurten tietokokonaisuuksien käsittely tietokannoista tai tiedostojärjestelmistä.
- Reaaliaikaiset datavirrat: Datan käsittely web-soketeista, viestijonoista tai anturiverkoista.
- API-kulutus: Datan haku ja käsittely API:sta, jotka palauttavat kohteita virtana.
- Kuvan- ja videonkäsittely: Suurten mediatiedostojen käsittely paloina.
- Lokianalyysi: Suurten lokitiedostojen analysointi rivi riviltä.
Esimerkki - kansainvälinen pörssidata: Kuvittele sovellus, joka hakee reaaliaikaisia pörssikursseja eri kansainvälisistä pörsseistä. Asynkronista iteraattoria voidaan käyttää datan virtauttamiseen, ja asynkroninen 'for each' voi käsitellä jokaisen kurssinoteerauksen päivittäen käyttöliittymän viimeisimmillä hinnoilla. Tätä voidaan käyttää näyttämään seuraavien kaltaisten yritysten ajantasaisia osakekursseja:
- Tencent (Kiina): Suuren kansainvälisen teknologiayrityksen osaketietojen haku
- Tata Consultancy Services (Intia): Johtavan IT-palveluyrityksen osakekurssipäivitysten näyttäminen
- Samsung Electronics (Etelä-Korea): Maailmanlaajuisen elektroniikkavalmistajan osakekurssien esittäminen
- Toyota Motor Corporation (Japani): Kansainvälisen autonvalmistajan osakekurssien seuranta
Yhteenveto
Asynkroniset iteraattorit ja asynkroninen 'for each' -apuri tarjoavat tehokkaan ja elegantin tavan käsitellä datavirtoja asynkronisesti JavaScriptissä. Kapseloimalla iterointilogiikan voit yksinkertaistaa koodiasi, parantaa luettavuutta ja tehostaa sovellustesi suorituskykyä. Käsittelemällä virheitä, hallitsemalla rinnakkaisuutta ja mahdollistamalla peruutuksen voit luoda vankkoja ja skaalautuvia asynkronisia datankäsittelyputkia.