Magyar

Ismerje meg a JavaScript Async Iterator segédfüggvényeket az adatfolyam-feldolgozás forradalmasításához. Tanulja meg az aszinkron adatfolyamok hatékony kezelését a map, filter, take, drop és egyéb metódusokkal.

JavaScript Async Iterator segédfüggvények: Hatékony adatfolyam-feldolgozás modern alkalmazásokhoz

A modern JavaScript fejlesztésben gyakori követelmény az aszinkron adatfolyamok kezelése. Legyen szó API-ból történő adatlekérésről, nagy fájlok feldolgozásáról vagy valós idejű események kezeléséről, az aszinkron adatok hatékony kezelése kulcsfontosságú. A JavaScript Async Iterator segédfüggvényei erőteljes és elegáns módot kínálnak ezen adatfolyamok feldolgozására, funkcionális és kompozíciós megközelítést biztosítva az adatmanipulációhoz.

Mik azok az aszinkron iterátorok és aszinkron iterálható objektumok?

Mielőtt belemerülnénk az Async Iterator segédfüggvényekbe, értsük meg az alapul szolgáló koncepciókat: az aszinkron iterátorokat és az aszinkron iterálható objektumokat.

Az aszinkron iterálható objektum (Async Iterable) egy olyan objektum, amely meghatározza az értékein való aszinkron iterálás módját. Ezt a @@asyncIterator metódus implementálásával teszi, amely egy aszinkron iterátort (Async Iterator) ad vissza.

Az aszinkron iterátor (Async Iterator) egy olyan objektum, amely rendelkezik egy next() metódussal. Ez a metódus egy promise-t ad vissza, amely egy két tulajdonsággal rendelkező objektumra oldódik fel:

Íme egy egyszerű példa:


async function* generateSequence(end) {
  for (let i = 1; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 500)); // Aszinkron művelet szimulálása
    yield i;
  }
}

const asyncIterable = generateSequence(5);

(async () => {
  for await (const value of asyncIterable) {
    console.log(value); // Kimenet: 1, 2, 3, 4, 5 (mindegyik között 500ms késleltetéssel)
  }
})();

Ebben a példában a generateSequence egy aszinkron generátorfüggvény, amely aszinkron módon hoz létre egy számsorozatot. A for await...of ciklus az aszinkron iterálható objektumból származó értékek feldolgozására szolgál.

Az Async Iterator segédfüggvények bemutatása

Az Async Iterator segédfüggvények kiterjesztik az aszinkron iterátorok funkcionalitását, egy sor metódust biztosítva az aszinkron adatfolyamok átalakítására, szűrésére és manipulálására. Lehetővé teszik a funkcionális és kompozíciós programozási stílust, megkönnyítve az összetett adatfeldolgozási láncok kiépítését.

A legfontosabb Async Iterator segédfüggvények a következők:

Nézzük meg az egyes segédfüggvényeket példákkal.

map()

A map() segédfüggvény egy megadott függvény segítségével átalakítja az aszinkron iterálható objektum minden elemét. Visszaad egy új, az átalakított értékeket tartalmazó aszinkron iterálható objektumot.


async function* generateSequence(end) {
  for (let i = 1; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

const asyncIterable = generateSequence(5);

const doubledIterable = asyncIterable.map(x => x * 2);

(async () => {
  for await (const value of doubledIterable) {
    console.log(value); // Kimenet: 2, 4, 6, 8, 10 (100ms késleltetéssel)
  }
})();

Ebben a példában a map(x => x * 2) megduplázza a sorozat minden számát.

filter()

A filter() segédfüggvény egy megadott feltétel (predikátumfüggvény) alapján választ ki elemeket az aszinkron iterálható objektumból. Visszaad egy új aszinkron iterálható objektumot, amely csak a feltételnek megfelelő elemeket tartalmazza.


async function* generateSequence(end) {
  for (let i = 1; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

const asyncIterable = generateSequence(10);

const evenNumbersIterable = asyncIterable.filter(x => x % 2 === 0);

(async () => {
  for await (const value of evenNumbersIterable) {
    console.log(value); // Kimenet: 2, 4, 6, 8, 10 (100ms késleltetéssel)
  }
})();

Ebben a példában a filter(x => x % 2 === 0) csak a páros számokat választja ki a sorozatból.

take()

A take() segédfüggvény visszaadja az aszinkron iterálható objektum első N elemét. Egy új aszinkron iterálható objektumot ad vissza, amely csak a megadott számú elemet tartalmazza.


async function* generateSequence(end) {
  for (let i = 1; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

const asyncIterable = generateSequence(5);

const firstThreeIterable = asyncIterable.take(3);

(async () => {
  for await (const value of firstThreeIterable) {
    console.log(value); // Kimenet: 1, 2, 3 (100ms késleltetéssel)
  }
})();

Ebben a példában a take(3) a sorozat első három számát választja ki.

drop()

A drop() segédfüggvény kihagyja az aszinkron iterálható objektum első N elemét, és a többit adja vissza. Egy új aszinkron iterálható objektumot ad vissza, amely a fennmaradó elemeket tartalmazza.


async function* generateSequence(end) {
  for (let i = 1; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

const asyncIterable = generateSequence(5);

const afterFirstTwoIterable = asyncIterable.drop(2);

(async () => {
  for await (const value of afterFirstTwoIterable) {
    console.log(value); // Kimenet: 3, 4, 5 (100ms késleltetéssel)
  }
})();

Ebben a példában a drop(2) kihagyja a sorozat első két számát.

toArray()

A toArray() segédfüggvény feldolgozza a teljes aszinkron iterálható objektumot, és az összes elemet egy tömbbe gyűjti. Visszaad egy promise-t, amely egy, az összes elemet tartalmazó tömbre oldódik fel.


async function* generateSequence(end) {
  for (let i = 1; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

const asyncIterable = generateSequence(5);

(async () => {
  const numbersArray = await asyncIterable.toArray();
  console.log(numbersArray); // Kimenet: [1, 2, 3, 4, 5]
})();

Ebben a példában a toArray() a sorozat összes számát egy tömbbe gyűjti.

forEach()

A forEach() segédfüggvény minden elemre egyszer végrehajt egy megadott függvényt az aszinkron iterálható objektumban. *Nem* ad vissza új aszinkron iterálható objektumot, hanem mellékhatásként hajtja végre a függvényt. Ez hasznos lehet olyan műveletekhez, mint a naplózás vagy egy felhasználói felület frissítése.


async function* generateSequence(end) {
  for (let i = 1; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

const asyncIterable = generateSequence(3);

(async () => {
  await asyncIterable.forEach(value => {
    console.log("Value:", value);
  });
  console.log("forEach completed");
})();
// Kimenet: Value: 1, Value: 2, Value: 3, forEach completed

some()

A some() segédfüggvény azt teszteli, hogy az aszinkron iterálható objektumban legalább egy elem megfelel-e a megadott függvény által implementált tesztnek. Visszaad egy promise-t, amely egy logikai értékre oldódik fel (true, ha legalább egy elem megfelel a feltételnek, egyébként false).


async function* generateSequence(end) {
  for (let i = 1; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

const asyncIterable = generateSequence(5);

(async () => {
  const hasEvenNumber = await asyncIterable.some(x => x % 2 === 0);
  console.log("Has even number:", hasEvenNumber); // Kimenet: Has even number: true
})();

every()

Az every() segédfüggvény azt teszteli, hogy az aszinkron iterálható objektum összes eleme megfelel-e a megadott függvény által implementált tesztnek. Visszaad egy promise-t, amely egy logikai értékre oldódik fel (true, ha minden elem megfelel a feltételnek, egyébként false).


async function* generateSequence(end) {
  for (let i = 2; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

const asyncIterable = generateSequence(4);

(async () => {
  const areAllEven = await asyncIterable.every(x => x % 2 === 0);
  console.log("Are all even:", areAllEven); // Kimenet: Are all even: true
})();

find()

A find() segédfüggvény visszaadja az aszinkron iterálható objektum első elemét, amely megfelel a megadott tesztelő függvénynek. Ha egyetlen érték sem felel meg a tesztelő függvénynek, undefined értéket ad vissza. Visszaad egy promise-t, amely a talált elemre vagy undefined-ra oldódik fel.


async function* generateSequence(end) {
  for (let i = 1; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

const asyncIterable = generateSequence(5);

(async () => {
  const firstEven = await asyncIterable.find(x => x % 2 === 0);
  console.log("First even number:", firstEven); // Kimenet: First even number: 2
})();

reduce()

A reduce() segédfüggvény egy felhasználó által megadott "reducer" visszahívási függvényt hajt végre az aszinkron iterálható objektum minden elemén sorrendben, átadva az előző elem számításából származó visszatérési értéket. A reducer összes elemen való futtatásának végeredménye egyetlen érték. Visszaad egy promise-t, amely a végső felhalmozott értékre oldódik fel.


async function* generateSequence(end) {
  for (let i = 1; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

const asyncIterable = generateSequence(5);

(async () => {
  const sum = await asyncIterable.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
  console.log("Sum:", sum); // Kimenet: Sum: 15
})();

Gyakorlati példák és felhasználási esetek

Az Async Iterator segédfüggvények számos forgatókönyvben értékesek. Nézzünk meg néhány gyakorlati példát:

1. Adatok feldolgozása streaming API-ból

Képzelje el, hogy egy valós idejű adatvizualizációs műszerfalat épít, amely egy streaming API-ból kap adatokat. Az API folyamatosan küld frissítéseket, és ezeket fel kell dolgoznia a legfrissebb információk megjelenítéséhez.


async function* fetchDataFromAPI(url) {
  let response = await fetch(url);

  if (!response.body) {
    throw new Error("ReadableStream not supported in this environment");
  }

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) {
        break;
      }
      const chunk = decoder.decode(value);
      // Feltételezve, hogy az API újsorokkal elválasztott JSON objektumokat küld
      const lines = chunk.split('\n');
      for (const line of lines) {
        if (line.trim() !== '') {
          yield JSON.parse(line);
        }
      }
    }
  } finally {
    reader.releaseLock();
  }
}

const apiURL = 'https://example.com/streaming-api'; // Cserélje le a saját API URL-jére
const dataStream = fetchDataFromAPI(apiURL);

// Az adatfolyam feldolgozása
(async () => {
  for await (const data of dataStream.filter(item => item.type === 'metric').map(item => ({ timestamp: item.timestamp, value: item.value }))) {
    console.log('Feldolgozott adat:', data);
    // A műszerfal frissítése a feldolgozott adatokkal
  }
})();

Ebben a példában a fetchDataFromAPI adatokat kér le egy streaming API-ból, elemzi a JSON objektumokat, és aszinkron iterálható objektumként adja vissza őket. A filter segédfüggvény csak a metrikákat választja ki, a map segédfüggvény pedig a kívánt formátumra alakítja az adatokat a műszerfal frissítése előtt.

2. Nagy fájlok olvasása és feldolgozása

Tegyük fel, hogy egy nagy, ügyféladatokat tartalmazó CSV-fájlt kell feldolgoznia. Ahelyett, hogy a teljes fájlt a memóriába töltené, az Async Iterator segédfüggvényekkel darabonként dolgozhatja fel.


async function* readLinesFromFile(filePath) {
  const file = await fsPromises.open(filePath, 'r');

  try {
    let buffer = Buffer.alloc(1024);
    let fileOffset = 0;
    let remainder = '';

    while (true) {
      const { bytesRead } = await file.read(buffer, 0, buffer.length, fileOffset);
      if (bytesRead === 0) {
        if (remainder) {
          yield remainder;
        }
        break;
      }

      fileOffset += bytesRead;
      const chunk = buffer.toString('utf8', 0, bytesRead);
      const lines = chunk.split('\n');

      lines[0] = remainder + lines[0];
      remainder = lines.pop() || '';

      for (const line of lines) {
        yield line;
      }
    }
  } finally {
    await file.close();
  }
}

const filePath = './customer_data.csv'; // Cserélje le a saját fájlútvonalára
const lines = readLinesFromFile(filePath);

// A sorok feldolgozása
(async () => {
  for await (const customerData of lines.drop(1).map(line => line.split(',')).filter(data => data[2] === 'USA')) {
    console.log('Amerikai ügyfél:', customerData);
    // Az amerikai ügyféladatok feldolgozása
  }
})();

Ebben a példában a readLinesFromFile soronként olvassa a fájlt, és minden sort aszinkron iterálható objektumként ad vissza. A drop(1) segédfüggvény kihagyja a fejléc sort, a map segédfüggvény oszlopokra bontja a sort, a filter segédfüggvény pedig csak az amerikai ügyfeleket választja ki.

3. Valós idejű események kezelése

Az Async Iterator segédfüggvények valós idejű események kezelésére is használhatók, például WebSocketekből származó események esetén. Létrehozhat egy aszinkron iterálható objektumot, amely az események beérkezésekor bocsátja ki azokat, majd a segédfüggvényekkel feldolgozhatja ezeket az eseményeket.


async function* createWebSocketStream(url) {
  const ws = new WebSocket(url);

  yield new Promise((resolve, reject) => {
      ws.onopen = () => {
          resolve();
      };
      ws.onerror = (error) => {
          reject(error);
      };
  });

  try {
    while (ws.readyState === WebSocket.OPEN) {
      yield new Promise((resolve, reject) => {
        ws.onmessage = (event) => {
          resolve(JSON.parse(event.data));
        };
        ws.onerror = (error) => {
          reject(error);
        };
        ws.onclose = () => {
           resolve(null); // A kapcsolat bezárásakor null értékkel oldódik fel
        }
      });

    }
  } finally {
    ws.close();
  }
}

const websocketURL = 'wss://example.com/events'; // Cserélje le a saját WebSocket URL-jére
const eventStream = createWebSocketStream(websocketURL);

// Az eseményfolyam feldolgozása
(async () => {
  for await (const event of eventStream.filter(event => event.type === 'user_login').map(event => ({ userId: event.userId, timestamp: event.timestamp }))) {
    console.log('Felhasználói bejelentkezési esemény:', event);
    // A felhasználói bejelentkezési esemény feldolgozása
  }
})();

Ebben a példában a createWebSocketStream egy aszinkron iterálható objektumot hoz létre, amely a WebSocketről kapott eseményeket bocsátja ki. A filter segédfüggvény csak a felhasználói bejelentkezési eseményeket választja ki, a map segédfüggvény pedig a kívánt formátumra alakítja az adatokat.

Az Async Iterator segédfüggvények használatának előnyei

Böngésző- és futtatókörnyezeti támogatás

Az Async Iterator segédfüggvények még viszonylag új funkciónak számítanak a JavaScriptben. 2024 végén a TC39 szabványosítási folyamat 3. szakaszában vannak, ami azt jelenti, hogy valószínűleg a közeljövőben szabványosítva lesznek. Azonban még nem támogatottak natívan minden böngészőben és Node.js verzióban.

Böngészőtámogatás: A modern böngészők, mint a Chrome, a Firefox, a Safari és az Edge, fokozatosan vezetik be az Async Iterator segédfüggvények támogatását. A legfrissebb böngészőkompatibilitási információkat olyan webhelyeken ellenőrizheti, mint a Can I use..., hogy lássa, mely böngészők támogatják ezt a funkciót.

Node.js támogatás: A Node.js újabb verziói (v18 és újabbak) kísérleti támogatást nyújtanak az Async Iterator segédfüggvényekhez. Használatukhoz lehet, hogy a Node.js-t a --experimental-async-iterator kapcsolóval kell futtatni.

Polyfillek: Ha olyan környezetekben kell használnia az Async Iterator segédfüggvényeket, amelyek natívan nem támogatják őket, használhat polyfillt. A polyfill egy kódrészlet, amely biztosítja a hiányzó funkcionalitást. Számos polyfill könyvtár érhető el az Async Iterator segédfüggvényekhez; egy népszerű lehetőség a core-js könyvtár.

Egyéni aszinkron iterátorok implementálása

Bár az Async Iterator segédfüggvények kényelmes módot kínálnak a meglévő aszinkron iterálható objektumok feldolgozására, néha szükség lehet saját egyéni aszinkron iterátorok létrehozására. Ez lehetővé teszi, hogy különböző forrásokból, például adatbázisokból, API-kból vagy fájlrendszerekből származó adatokat streaming módon kezeljen.

Egyéni aszinkron iterátor létrehozásához implementálnia kell a @@asyncIterator metódust egy objektumon. Ennek a metódusnak egy olyan objektumot kell visszaadnia, amely rendelkezik egy next() metódussal. A next() metódusnak egy promise-t kell visszaadnia, amely egy value és done tulajdonságokkal rendelkező objektumra oldódik fel.

Íme egy példa egy egyéni aszinkron iterátorra, amely adatokat kér le egy lapozott API-ból:


async function* fetchPaginatedData(baseURL) {
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const url = `${baseURL}?page=${page}`;
    const response = await fetch(url);
    const data = await response.json();

    if (data.results.length === 0) {
      hasMore = false;
      break;
    }

    for (const item of data.results) {
      yield item;
    }

    page++;
  }
}

const apiBaseURL = 'https://api.example.com/data'; // Cserélje le a saját API URL-jére
const paginatedData = fetchPaginatedData(apiBaseURL);

// A lapozott adatok feldolgozása
(async () => {
  for await (const item of paginatedData) {
    console.log('Elem:', item);
    // Az elem feldolgozása
  }
})();

Ebben a példában a fetchPaginatedData adatokat kér le egy lapozott API-ból, és minden egyes elemet visszaad, amint azt lekérte. Az aszinkron iterátor kezeli a lapozási logikát, ami megkönnyíti az adatok streaming módon történő feldolgozását.

Lehetséges kihívások és megfontolások

Bár az Async Iterator segédfüggvények számos előnnyel járnak, fontos tisztában lenni néhány lehetséges kihívással és megfontolással:

Az Async Iterator segédfüggvények használatának legjobb gyakorlatai

Ahhoz, hogy a legtöbbet hozza ki az Async Iterator segédfüggvényekből, vegye figyelembe a következő legjobb gyakorlatokat:

Haladó technikák

Egyéni segédfüggvények komponálása

Létrehozhat saját egyéni aszinkron iterátor segédfüggvényeket meglévő segédfüggvények komponálásával vagy újak létrehozásával a semmiből. Ez lehetővé teszi, hogy a funkcionalitást saját specifikus igényeihez igazítsa és újrafelhasználható komponenseket hozzon létre.


async function* takeWhile(asyncIterable, predicate) {
  for await (const value of asyncIterable) {
    if (!predicate(value)) {
      break;
    }
    yield value;
  }
}

// Példa használat:
async function* generateSequence(end) {
  for (let i = 1; i <= end; i++) {
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

const asyncIterable = generateSequence(10);
const firstFive = takeWhile(asyncIterable, x => x <= 5);

(async () => {
  for await (const value of firstFive) {
    console.log(value);
  }
})();

Több aszinkron iterálható objektum kombinálása

Több aszinkron iterálható objektumot egyetlen aszinkron iterálható objektummá kombinálhat olyan technikákkal, mint a zip vagy a merge. Ez lehetővé teszi, hogy több forrásból származó adatokat egyszerre dolgozzon fel.


async function* zip(asyncIterable1, asyncIterable2) {
    const iterator1 = asyncIterable1[Symbol.asyncIterator]();
    const iterator2 = asyncIterable2[Symbol.asyncIterator]();

    while (true) {
        const result1 = await iterator1.next();
        const result2 = await iterator2.next();

        if (result1.done || result2.done) {
            break;
        }

        yield [result1.value, result2.value];
    }
}

// Példa használat:
async function* generateSequence1(end) {
    for (let i = 1; i <= end; i++) {
        yield i;
    }
}

async function* generateSequence2(end) {
    for (let i = 10; i <= end + 9; i++) {
        yield i;
    }
}

const iterable1 = generateSequence1(5);
const iterable2 = generateSequence2(5);

(async () => {
    for await (const [value1, value2] of zip(iterable1, iterable2)) {
        console.log(value1, value2);
    }
})();

Összegzés

A JavaScript Async Iterator segédfüggvényei erőteljes és elegáns módot kínálnak az aszinkron adatfolyamok feldolgozására. Funkcionális és kompozíciós megközelítést biztosítanak az adatmanipulációhoz, megkönnyítve az összetett adatfeldolgozási láncok kiépítését. Az aszinkron iterátorok és aszinkron iterálható objektumok alapkoncepcióinak megértésével és a különböző segédmetódusok elsajátításával jelentősen javíthatja aszinkron JavaScript kódjának hatékonyságát és karbantarthatóságát. Ahogy a böngésző- és futtatókörnyezeti támogatás tovább növekszik, az Async Iterator segédfüggvények a modern JavaScript fejlesztők nélkülözhetetlen eszközévé válnak.