Avastage JavaScripti asünkroonsed generaatorid tõhusaks voogude töötlemiseks. Õppige looma, tarbima ja rakendama täiustatud mustreid asünkroonsete andmete käsitlemiseks.
JavaScripti asünkroonsed generaatorid: voogude töötlemise mustrite valdamine
JavaScripti asünkroonsed generaatorid pakuvad võimsat mehhanismi asünkroonsete andmevoogude tõhusaks käsitlemiseks. Need ühendavad asünkroonse programmeerimise võimekuse iteraatorite elegantsiga, võimaldades teil töödelda andmeid nende kättesaadavaks muutumisel, ilma et see blokeeriks põhilõime. See lähenemine on eriti kasulik stsenaariumide puhul, mis hõlmavad suuri andmehulki, reaalajas andmevooge ja keerukaid andmeteisendusi.
Asünkroonsete generaatorite ja asünkroonsete iteraatorite mõistmine
Enne voogude töötlemise mustritesse süvenemist on oluline mõista asünkroonsete generaatorite ja asünkroonsete iteraatorite põhimõisteid.
Mis on asünkroonsed generaatorid?
Asünkroonne generaator on eriline funktsioonitüüp, mida saab peatada ja jätkata, võimaldades sel väärtusi asünkroonselt väljastada (yield). See defineeritakse süntaksiga async function*
. Erinevalt tavalistest generaatoritest saavad asünkroonsed generaatorid kasutada await
-i asünkroonsete operatsioonide käsitlemiseks generaatori funktsiooni sees.
Näide:
async function* generateSequence(start, end) {
for (let i = start; i <= end; i++) {
await new Promise(resolve => setTimeout(resolve, 500)); // Simulate asynchronous delay
yield i;
}
}
Selles näites on generateSequence
asünkroonne generaator, mis väljastab numbrite jada alates väärtusest start
kuni väärtuseni end
, 500ms viivitusega iga numbri vahel. Võtmesõna await
tagab, et generaator peatub, kuni lubadus (promise) laheneb (simuleerides asünkroonset operatsiooni).
Mis on asünkroonsed iteraatorid?
Asünkroonne iteraator on objekt, mis vastab asünkroonse iteraatori protokollile. Sellel on next()
meetod, mis tagastab lubaduse (promise). Kui lubadus laheneb, pakub see objekti kahe omadusega: value
(väljastatud väärtus) ja done
(tõeväärtus, mis näitab, kas iteraator on jada lõppu jõudnud).
Asünkroonsed generaatorid loovad automaatselt asünkroonseid iteraatoreid. Asünkroonse generaatori väljastatud väärtusi saab itereerida, kasutades tsüklit for await...of
.
Näide:
async function consumeSequence() {
for await (const num of generateSequence(1, 5)) {
console.log(num);
}
}
consumeSequence(); // Väljund: 1 (500ms pärast), 2 (1000ms pärast), 3 (1500ms pärast), 4 (2000ms pärast), 5 (2500ms pärast)
Tsükkel for await...of
itereerib asünkroonselt läbi generateSequence
asünkroonse generaatori väljastatud väärtuste, printides iga numbri konsooli.
Voogude töötlemise mustrid asünkroonsete generaatoritega
Asünkroonsed generaatorid on uskumatult mitmekülgsed erinevate voogude töötlemise mustrite rakendamiseks. Siin on mõned levinud ja võimsad mustrid:
1. Andmeallika abstraktsioon
Asünkroonsed generaatorid suudavad abstraheerida erinevate andmeallikate keerukuse, pakkudes ühtset liidest andmetele juurdepääsuks, sõltumata nende päritolust. See on eriti kasulik API-de, andmebaaside või failisüsteemidega tegelemisel.
Näide: Andmete pärimine API-st
async function* fetchUsers(apiUrl) {
let page = 1;
while (true) {
const url = `${apiUrl}?page=${page}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.length === 0) {
return; // Rohkem andmeid ei ole
}
for (const user of data) {
yield user;
}
page++;
}
}
async function processUsers() {
const userGenerator = fetchUsers('https://api.example.com/users'); // Asenda oma API otspunktiga
for await (const user of userGenerator) {
console.log(user.name);
// Töötle iga kasutajat
}
}
processUsers();
Selles näites hangib fetchUsers
asünkroonne generaator kasutajaid API otspunktist, käsitledes automaatselt lehekülgedeks jaotamist. Funktsioon processUsers
tarbib andmevoogu ja töötleb iga kasutajat.
Rahvusvahelistamise märkus: API-dest andmete pärimisel veenduge, et API otspunkt järgiks rahvusvahelistamise standardeid (nt toetaks keelekoode ja piirkondlikke sätteid), et pakkuda ühtlast kasutuskogemust kasutajatele üle maailma.
2. Andmete teisendamine ja filtreerimine
Asünkroonseid generaatoreid saab kasutada andmevoogude teisendamiseks ja filtreerimiseks, rakendades teisendusi asünkroonselt ilma põhilõime blokeerimata.
Näide: Logikirjete filtreerimine ja teisendamine
async function* filterAndTransformLogs(logGenerator, filterKeyword) {
for await (const logEntry of logGenerator) {
if (logEntry.message.includes(filterKeyword)) {
const transformedEntry = {
timestamp: logEntry.timestamp,
level: logEntry.level,
message: logEntry.message.toUpperCase(),
};
yield transformedEntry;
}
}
}
async function* readLogsFromFile(filePath) {
// Simuleerib logide lugemist failist asünkroonselt
const logs = [
{ timestamp: '2024-01-01T00:00:00', level: 'INFO', message: 'System started' },
{ timestamp: '2024-01-01T00:00:05', level: 'WARN', message: 'Low memory warning' },
{ timestamp: '2024-01-01T00:00:10', level: 'ERROR', message: 'Database connection failed' },
];
for (const log of logs) {
await new Promise(resolve => setTimeout(resolve, 100)); // Simuleerib asünkroonset lugemist
yield log;
}
}
async function processFilteredLogs() {
const logGenerator = readLogsFromFile('logs.txt');
const filteredLogs = filterAndTransformLogs(logGenerator, 'ERROR');
for await (const log of filteredLogs) {
console.log(log);
}
}
processFilteredLogs();
Selles näites filtreerib filterAndTransformLogs
logikirjeid märksõna alusel ja teisendab sobivad kirjed suurtähtedeks. Funktsioon readLogsFromFile
simuleerib logikirjete asünkroonset lugemist failist.
3. Samaaegne töötlemine
Asünkroonseid generaatoreid saab kombineerida Promise.all
-i või sarnaste samaaegsuse mehhanismidega, et töödelda andmeid samaaegselt, parandades arvutusmahukate ülesannete jõudlust.
Näide: Piltide samaaegne töötlemine
async function* generateImagePaths(imageUrls) {
for (const url of imageUrls) {
yield url;
}
}
async function processImage(imageUrl) {
// Simuleerib pilditöötlust
await new Promise(resolve => setTimeout(resolve, 200));
console.log(`Processed image: ${imageUrl}`);
return `Processed: ${imageUrl}`;
}
async function processImagesConcurrently(imageUrls, concurrencyLimit) {
const imageGenerator = generateImagePaths(imageUrls);
const processingPromises = [];
async function processNextImage() {
const { value, done } = await imageGenerator.next();
if (done) {
return;
}
const processingPromise = processImage(value);
processingPromises.push(processingPromise);
processingPromise.finally(() => {
// Eemalda lõpetatud lubadus massiivist
processingPromises.splice(processingPromises.indexOf(processingPromise), 1);
// Alusta järgmise pildi töötlemist, kui võimalik
if (processingPromises.length < concurrencyLimit) {
processNextImage();
}
});
if (processingPromises.length < concurrencyLimit) {
processNextImage();
}
}
// Alusta esialgseid samaaegseid protsesse
for (let i = 0; i < concurrencyLimit && i < imageUrls.length; i++) {
processNextImage();
}
// Oota, kuni kõik lubadused lahenevad, enne tagastamist
await Promise.all(processingPromises);
console.log('All images processed.');
}
const imageUrls = [
'https://example.com/image1.jpg',
'https://example.com/image2.jpg',
'https://example.com/image3.jpg',
'https://example.com/image4.jpg',
'https://example.com/image5.jpg',
];
processImagesConcurrently(imageUrls, 2);
Selles näites väljastab generateImagePaths
piltide URL-ide voo. Funktsioon processImage
simuleerib pilditöötlust. processImagesConcurrently
töötleb pilte samaaegselt, piirates samaaegsete protsesside arvu kahega, kasutades lubaduste massiivi. See on oluline süsteemi ülekoormamise vältimiseks. Iga pilti töödeldakse asünkroonselt setTimeouti kaudu. Lõpuks tagab Promise.all
, et kõik protsessid lõppevad enne üldise operatsiooni lõpetamist.
4. Vasturõhu käsitlemine
Vasturõhk on voogude töötlemisel ülioluline mõiste, eriti kui andmete tootmise kiirus ületab andmete tarbimise kiiruse. Asünkroonseid generaatoreid saab kasutada vasturõhu mehhanismide rakendamiseks, vältides tarbija ülekoormamist.
Näide: Kiirusepiiraja rakendamine
async function* applyRateLimit(dataGenerator, interval) {
for await (const data of dataGenerator) {
await new Promise(resolve => setTimeout(resolve, interval));
yield data;
}
}
async function* generateData() {
let i = 0;
while (true) {
await new Promise(resolve => setTimeout(resolve, 10)); // Simuleerib kiiret tootjat
yield `Data ${i++}`;
}
}
async function consumeData() {
const dataGenerator = generateData();
const rateLimitedData = applyRateLimit(dataGenerator, 500); // Piira ühe elemendini iga 500ms järel
for await (const data of rateLimitedData) {
console.log(data);
}
}
// consumeData(); // Ettevaatust, see töötab lõputult
Selles näites piirab applyRateLimit
kiirust, millega andmeid dataGenerator
-ist väljastatakse, tagades, et tarbija ei saa andmeid kiiremini, kui ta suudab neid töödelda.
5. Voogude kombineerimine
Asünkroonseid generaatoreid saab kombineerida keerukate andmekonveierite loomiseks. See võib olla kasulik andmete ühendamiseks mitmest allikast, keerukate teisenduste tegemiseks või hargnevate andmevoogude loomiseks.
Näide: Andmete ühendamine kahest API-st
async function* mergeStreams(stream1, stream2) {
const iterator1 = stream1();
const iterator2 = stream2();
let next1 = iterator1.next();
let next2 = iterator2.next();
while (!((await next1).done && (await next2).done)) {
if (!(await next1).done) {
yield (await next1).value;
next1 = iterator1.next();
}
if (!(await next2).done) {
yield (await next2).value;
next2 = iterator2.next();
}
}
}
async function* generateNumbers(limit) {
for (let i = 1; i <= limit; i++) {
await new Promise(resolve => setTimeout(resolve, 100));
yield i;
}
}
async function* generateLetters(limit) {
const letters = 'abcdefghijklmnopqrstuvwxyz';
for (let i = 0; i < limit; i++) {
await new Promise(resolve => setTimeout(resolve, 150));
yield letters[i];
}
}
async function processMergedData() {
const numberStream = () => generateNumbers(5);
const letterStream = () => generateLetters(3);
const mergedStream = mergeStreams(numberStream, letterStream);
for await (const item of mergedStream) {
console.log(item);
}
}
processMergedData();
Selles näites ühendab mergeStreams
andmed kahest asünkroonsest generaatorfunktsioonist, põimides nende väljundit. generateNumbers
ja generateLetters
on näidis-asünkroonsed generaatorid, mis pakuvad vastavalt numbrilisi ja tähestikulisi andmeid.
Täiustatud tehnikad ja kaalutlused
Kuigi asünkroonsed generaatorid pakuvad võimsat viisi asünkroonsete voogude käsitlemiseks, on oluline arvestada mõningate täiustatud tehnikate ja võimalike väljakutsetega.
Vigade käsitlemine
Nõuetekohane vigade käsitlemine on asünkroonses koodis ülioluline. Vigade sujuvaks käsitlemiseks saate asünkroonsetes generaatorites kasutada try...catch
plokke.
async function* safeGenerator() {
try {
// Asünkroonsed operatsioonid, mis võivad vigu visata
const data = await fetchData();
yield data;
} catch (error) {
console.error('Viga generaatoris:', error);
// Valikuliselt väljasta vea väärtus või lõpeta generaator
yield { error: error.message };
return; // Peata generaator
}
}
Tühistamine
Mõnel juhul võib olla vaja käimasolevat asünkroonset operatsiooni tühistada. Seda saab saavutada selliste tehnikate abil nagu AbortController.
async function* fetchWithCancellation(url, signal) {
try {
const response = await fetch(url, { signal });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
yield data;
} catch (error) {
if (error.name === 'AbortError') {
console.log('Päring tühistati');
return;
}
throw error;
}
}
const controller = new AbortController();
const { signal } = controller;
async function consumeData() {
const dataGenerator = fetchWithCancellation('https://api.example.com/data', signal); // Asenda oma API otspunktiga
setTimeout(() => {
controller.abort(); // Tühista päring 2 sekundi pärast
}, 2000);
try {
for await (const data of dataGenerator) {
console.log(data);
}
} catch (error) {
console.error('Viga tarbimise ajal:', error);
}
}
consumeData();
Mäluhaldus
Suurte andmevoogudega tegelemisel on oluline mälu tõhusalt hallata. Vältige suurte andmemahtude hoidmist mälus korraga. Asünkroonsed generaatorid aitavad oma olemuselt sellele kaasa, töödeldes andmeid tükkidena.
Silumine
Asünkroonse koodi silumine võib olla keeruline. Kasutage brauseri arendustööriistu või Node.js-i silureid, et oma koodi samm-sammult läbida ja muutujaid kontrollida.
Reaalsed rakendused
Asünkroonsed generaatorid on rakendatavad paljudes reaalsetes stsenaariumides:
- Reaalajas andmetöötlus: Andmete töötlemine WebSocketsist või server-sent events (SSE) kaudu.
- Suurte failide töötlemine: Suurte failide lugemine ja töötlemine tükkidena.
- Andmete voogedastus andmebaasidest: Suurte andmekogumite pärimine ja töötlemine andmebaasidest ilma kõike korraga mällu laadimata.
- API andmete agregeerimine: Andmete kombineerimine mitmest API-st ühtse andmevoo loomiseks.
- ETL (Extract, Transform, Load) konveierid: Keerukate andmekonveierite ehitamine andmeladude ja analüütika jaoks.
Näide: Suure CSV-faili töötlemine (Node.js)
const fs = require('fs');
const readline = require('readline');
async function* readCSV(filePath) {
const fileStream = fs.createReadStream(filePath);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity,
});
for await (const line of rl) {
// Töötle iga rida CSV-kirjena
const record = line.split(',');
yield record;
}
}
async function processCSV() {
const csvGenerator = readCSV('large_data.csv');
for await (const record of csvGenerator) {
// Töötle iga kirjet
console.log(record);
}
}
// processCSV();
Kokkuvõte
JavaScripti asünkroonsed generaatorid pakuvad võimsat ja elegantset viisi asünkroonsete andmevoogude käsitlemiseks. Valdades voogude töötlemise mustreid nagu andmeallika abstraktsioon, teisendamine, samaaegsus, vasturõhk ja voogude kombineerimine, saate ehitada tõhusaid ja skaleeritavaid rakendusi, mis käsitlevad tõhusalt suuri andmekogumeid ja reaalajas andmevooge. Vigade käsitlemise, tühistamise, mäluhalduse ja silumistehnikate mõistmine parandab veelgi teie võimet asünkroonsete generaatoritega töötada. Kuna asünkroonne programmeerimine muutub üha levinumaks, pakuvad asünkroonsed generaatorid väärtuslikku tööriistakomplekti kaasaegsetele JavaScripti arendajatele.
Võtke asünkroonsed generaatorid omaks, et avada asünkroonse andmetöötluse täielik potentsiaal oma JavaScripti projektides.