ru:https://highload.today/blogs/mozhno-vse-i-po-svoim-pravilam-kak-rabotaet-iteratsiya-v-javascript/ ua:https://highload.today/uk/blogs/mozhna-vse-i-za-svoyimi-pravilami-yak-pratsyuye-iteratsiya-v-javascript/
logo
Back-end      11/08/2022

Можна все, і за своїми правилами: як працює ітерація в JavaScript

Владислав Хирса BLOG

Senior Backend Developer | Node.js | NestJS | Express.js | SQL | NoSQL | AWS

Привіт усім. Мене звати Владислав Хирса, я — Software Engineer у Grid Dynamics. У цій статті я розповім вам багато корисного про ітерацію у JavaScript.

Ми всі використовуємо ітерацію для перебору масивів та об’єктів різних розмірів та для різних задач. Проте чи знаємо ми, як це працює? Як ми можемо змінити поведінку ітерації над нашими даними та в яких випадках це може знадобитись?

Якщо вам цікаві відповіді на ці запитання, тоді цей матеріал точно для вас.

Пригадаємо терміни

Для кращого розуміння пригадаємо декілька значень, а саме:

  • Перелічувані властивості enumerable properties — одна з трьох властивостей (configurable, enumerable, writable), які є в об’єкті. Щодо enumerable, то вона відповідає за те, чи можна повернути властивість у циклі for...in.

Наприклад, так:

Object.prototype.getType = function() {
  return this.type;
};

const object = {
  language: 'JavaScript',
  type: 'Lesson'
};

for (const key in object) {
  console.log(key); // JavaScript, Lesson, getType;
};
  • Ітерований об’єкт i terable object — це об’єкт, який побудований за певним патерном і має типову ітераційну поведінку. Він ітерується за допомогою spread syntax [...], for...of та for await...of. І в цій статті ми поговоримо саме про нього.
  • Протокол ітерації iterator protocol — це протокол, за допомогою якого ми можемо створити власні правила, за якими буде ітеруватись наш об’єкт. Якщо докладніше, то ітерувати ми зможемо такі типи даних, як string, array, object.

Головні правила iterator protocol — це:

  1. Ми повинні мати метод next().
  2. Онлайн-курс "Маркетингова аналітика" від Laba.
    Опануйте інструменти для дослідження ринку й аудиторії та проведення тестувань.Дізнайтесь, як оптимізувати поточні рекламні кампанії та будувати форкасти наступних.
    Детальніше про курс
  3. Метод next() повинен обов’язково повертати об’єкт типу iterable object. Він містить ключі value, який може мати будь-яке значення, та done, який може бути true або false.
  4. Коли ітерація завершилася, потрібно обов’язково повернути об’єкт { done: true }, адже тільки після цього наша ітерація закінчиться.
  5. Якщо done не буде повернуто або ж done буде мати будь-яке негативне значення, такі як undefined, null та інші, то наша ітерація буде безкінечною.

Створення ітеративної поведінки

Для створення ітеративної поведінки ми будемо використовувати наступні підходи:

  • Symbol.iterator з методом next().
  • Symbol.iterator з генератором.
  • Symbol.asyncIterator з генератором.

Тож по порядку, і почнемо ми з Symbol.iterator з методом next().

var array = [10, 20, 30, 40, 50];

array[Symbol.iterator] = function () {
  let i = 0;
  return {
    next() {
      i++
      return (i <= 5)
        ? { value: i, done }
        : { done: true }
    }
  }
};

for (const el of array) {
  console.log(el) // 1, 2, 3, 4, 5
};

Як це працює?

Курс Розмовної англійської від Englishdom.
Після цього курсу ви зможете спілкуватись з іноземцями і цікаво розкажете про себе.
Приєднатися

Спершу цикл for...of шукає у нашому об’єкті або серед тих, від яких він наслідується в prototype, чи має він Symbol.iterator. Якщо ні — то буде викликана автоматична помилка, а якщо так — тоді він викликає функцію, функція повертає наш об’єкт з методом next(), де і є вся наша основна логіка, та найголовніше, що при кожній ітерації циклу використовується метод next(), який і повертає значення за наших умов.

Symbol.iterator з генератором

З Symbol.iterator ми вже ознайомилися, а як щодо генераторів?

Генератор, якщо коротко, то це спосіб створення того ж iterable object для ітерації по iterator protocol, тобто в нього теж є метод next() і ітерується він циклом for...of, тільки синтаксис інший, та використовувати його у деяких випадках зручніше:

var it = {};

it[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

it.type = 'Lesson';
console.log(it); // { type: "Lesson", Symbol(Symbol.iterator): * Symbol.iterator() }
console.log([...it]); // [1, 2, 3]

У цій частинні ми змінили поведінку об’єкта та дали йому змогу бути ітерованим. Без Symbol.iterator була б помилка такого типу — Uncaught TypeError: it is not iterable при спробі ітерувати об’єкт. Також як ми бачимо, що до ітераційних даних ми не маємо доступу напряму, а з Symbol.iterator немає доступу до значень об’єкта. Це дуже зручно, і немає ніяких мутацій даних.

Symbol.asyncIterator з генератором

Дуже цікавий метод обробки даних, з допомогою якого ми можемо асинхронно обробляти дані, розбивати важкі і тривалі операції, що не перенавантажує та не блокує нашу систему. Тож хочу поділитися з вами, можливо, трохи складним, але корисним прикладом використання. Для зручності у цьому прикладі ми будемо використовувати типи.

Файл async-iterator.ts:

import { AsyncLimitIteratorParametersType } from './types';

export default class Iterator {
  public static generateAsyncLimitIterator(
    data: AsyncLimitIteratorParametersType
  ) {
    const {
      from,
      to,
      limit,
      asyncFunction,
      asyncFunctionParams

   } = data;

    return {
      async *[Symbol.asyncIterator]() {
        for (let now = from; now < to; now += limit) {
          yield await asyncFunction(
            asyncFunctionParams,
            { from, to, limit, now }
          );
        }
      }
    }
  }
};

Файл types.d.ts:

export type AsyncLimitIteratorParametersType = {
  from: number;
  to: number;
  limit: number;
  asyncFunction: Function;
  asyncFunctionParams: any;
}

export type IterationDataType = {
  from: number;
  to: number;
  limit: number;
  now: number;
};

Клас Iterator створений для асинхронної обробки (зміни) великих масивів даних.

Тож розберемо, як його можна використовувати і в чому його переваги.

Онлайн-курс "Чистий код та патерни проєктування" від robot_dreams.
Прискорюйте й спрощуйте процес розробки.Під менторством лектора з 15-річним досвідом ви навчитеся застосовувати 20+ шаблонів, опануєте рефакторинг і принципи чистого коду.
Детальніше

Клас Iterator

Отож, клас Iterator. У нього є один статичний метод generateAsyncLimitIterator, до якого ми маємо доступ, не використовуючи оператор new, метод generateAsyncLimitIterator приймає параметри:

  • from — з якого індексу змінювати масив;
  • to — по яку позицію за індексом змінювати масив;
  • limit — скільки елементів за одну ітерацію захопити та пройти;
  • asyncFunction — функція, в якій виконуються основні дії.

Наприклад, запит до бази даних для зміни об’єкта користувачів.
asyncFunctionParams — параметри, які приймає функція asyncFunction.

Приклад застосування:

const iteratorParams = {
  from: 0,
  to: 1000,
  limit: 10,
  asyncFunction: changeUsers,
  asyncFunctionParams: params
};

const iterateObj = Iterator.generateAsyncLimitIterator(iteratorParams);

for await (let value of iterateObj) {
  //
}

Після того, як ми створили асинхронний ітератор, ми просто використовуємо його в асинхронному циклі та не турбуємося про переповнений стек викликів і те, що заблокований Event Loop, а просто використовуємо надійний спосіб обробки даних.


На цьому все! Сподіваюся, що було корисно. Бажаю успіху та продуктивного кодування 😉

Курс English For IT: Communication від Enlgish4IT.
Почни легко працювати та спілкуватися з мультикультурними командами та міжнародними клієнтами. Отримайте знижку 10% за промокодом ITCENG.
Інформація про курс

Якщо ви знайшли помилку, будь ласка, виділіть фрагмент тексту та натисніть Ctrl+Enter.

Онлайн-курс "Проджект-менеджер в ІТ" від Laba.
Навчіться запускати, контролювати й успішно реалізовувати ІТ-проєкти. Пройти весь шлях проєктного управління на реальному кейсі вам допоможе PMD із 19-річним досвідом в ІТ.
Детальніше про курс

Цей матеріал – не редакційний, це – особиста думка його автора. Редакція може не поділяти цю думку.

Топ-5 найпопулярніших блогерів березня

PHP Developer в ScrumLaunch
Всего просмотровВсього переглядів
2434
#1
Всего просмотровВсього переглядів
2434
Founder at Shallwe, Python Software Engineer (Django/React)
Всего просмотровВсього переглядів
113
#2
Всего просмотровВсього переглядів
113
Career Consultant в GoIT
Всего просмотровВсього переглядів
95
#3
Всего просмотровВсього переглядів
95
CEO & Founder в Trustee
Всего просмотровВсього переглядів
94
#4
Всего просмотровВсього переглядів
94
Рейтинг блогерів

Найбільш обговорювані статті

Топ текстів

Ваша жалоба отправлена модератору

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: