Различные разработчики программного обеспечения несколько раз спрашивали меня о возможных вариантах использования итераторов / генераторов (I / G). Я также заметил, что у некоторых возникает вопрос, действительно ли они нам нужны, и я не виню их, поскольку в современном JavaScript есть множество способов делать одно и то же большую часть времени.

В этой статье я поделюсь двумя сценариями, в которых я использовал I / G, используя более простые примеры, но которые имеют практический смысл и важность. Это не объяснение того, что такое итераторы, итерируемые объекты и генераторы или как они работают. Есть хорошие источники, где это можно узнать, например, https://www.youtube.com/watch?v=ategZqxHkz4, или, если вы читаете, https://developer.mozilla.org/en -US / docs / Web / JavaScript / Guide / Iterators_and_Generators , или мое любимое объяснение: Понимание ECMAScript 6: Полное руководство для разработчиков JavaScript от Николаса К. Закаса. Не разочаровывайтесь и не расстраивайтесь, если сначала вы не можете все понять; очень умные люди, включая Дугласа Крокфорда, признали, что это сложная тема. Поэтому я решил написать эту статью, чтобы рассказать, как и почему я их использовал, на всякий случай, если мой личный опыт может кому-то помочь.

Сценарий первого случая: настраиваемые повторяющиеся последовательности.

Мне нужна коллекция чисел, которая при повторении дает мне только четные числа, и я хочу иметь возможность инкапсулировать логику выбора четных чисел в свою собственную коллекцию, чтобы код, использующий ее, не должен заботиться о это.

Функция EvenNumbers принимает необязательный массив, который позволяет вам инициализировать внутренний массив, который используется для обслуживания всех элементов / элементов, которые передаются сначала или вставляются позже. Он возвращает неизменяемый объект, который имеет два свойства: функцию для добавления нового элемента и символ, который в этом случае используется для реализации итератора по умолчанию для настраиваемой коллекции.

Тогда мы можем использовать эту функцию так:

Мы создали настраиваемую коллекцию, инициализированную массивом, содержащим три элемента, а позже мы добавляем к ней еще одно число. Затем мы перебираем коллекцию и записываем каждый элемент в консоль браузера. Если вы попробуете, вы должны увидеть только цифры 2 и 10, распечатанные на консоли. Прелесть этого кода в том, что потребитель функции EvenNumbers знает, чего ожидать, не беспокоясь о логике выбора четных чисел, поскольку задействованная логика была инкапсулирована самой функцией EvenNumbers. . Я знаю, что вы, вероятно, думаете, что поддерживаемость и повторное использование кода выиграют от такого подхода, я думаю то же самое. Это особенно верно, когда логика итераций намного сложнее, чем простой выбор четных чисел.

Если вы предпочитаете, чтобы все эти четные числа были в новом массиве только с одной строкой кода, вы могли бы просто сделать это:

Чтобы проверить, напечатайте evenValues ​​ в консоли.

Второй сценарий: написание асинхронного кода, который выглядит синхронным.

Асинхронные API очень распространены, например, возьмите функцию readFile из Node.js (https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback). Идея проста: после входных параметров функция принимает в качестве аргумента другую функцию, называемую обратным вызовом, которая будет выполнена, когда будет готов результат или действие будет завершено. В случае readFile (и других функций в Node.js) функция обратного вызова выполняется путем передачи ошибки, если что-то пошло не так при чтении файла с диска, или содержимого файла, если все прошло нормально.

Мы собираемся использовать функцию fetchData, которая работает так же, как и readFile:

Чтобы смоделировать асинхронную операцию, для выполнения которой требуется некоторое время, мы использовали setTimeout, и переданная ему лямбда-функция будет выполняться примерно через 1 секунду (технически она не выполняется примерно через 1 секунду. , он сначала помещается в очередь асинхронных заданий, а когда приходит его очередь, он запускается). Эта функция пытается найти человека на основе предоставленного идентификатора, если она его находит, вызывается обратный вызов, передавая ему человека. С другой стороны, если указанный идентификатор не принадлежит никому, в функцию обратного вызова передается ошибка. fetchData работает очень похоже на readFile, и мы могли бы реализовать реальную выборку, вызвав некоторый API в Интернете для получения данных, но я хотел, чтобы все было просто и понятно. -дом.

Типичный способ использования этой функции:

Мы вызвали fetchData и передали значение 1 в качестве идентификатора, мы также передали функцию обратного вызова в качестве второго аргумента. Таким образом, если человек найден, мы записываем его данные в консоль, если нет, мы выдаем ошибку.

Я знаю, что вы знаете, что этот асинхронный способ работы, включая логику обработки ошибок, может стать очень беспорядочным и беспорядочным, что приведет к тому, что известно как «Ад обратного вызова». Вот почему появились лучшие альтернативы, такие как Promises и Observer / Observables. При использовании обещаний вы связываете вызовы методов с помощью then и catch, что не делает ваш код синхронным и не позволяет вам рассуждать об этом таким образом. Чтобы преодолеть это, async / await сделал это в JavaScript, который под капотом использует Promises, но об этом позаботились вы, поэтому вы можете писать асинхронный код, который выглядит синхронным. К сожалению, даже когда все основные браузеры поддерживают async / await, он поддерживается не везде; вы можете использовать Babel, чтобы заставить его работать, когда он не работает, но сгенерированный код огромен, я видел, как async функция идет с 5 строк до более 60 после компиляции Babel.

Все это приводит к моему второму варианту использования, когда я использовал I / G, пишу асинхронный код, который выглядит синхронным, пожалуйста, без присмотра.

Мы написали оболочку вокруг fetchData, которая представляет собой не что иное, как функцию, которая принимает идентификатор в качестве параметра и возвращает функцию, которая принимает обратный вызов и выполняет fetchData, передав оба , идентификатор и обратный вызов.

Сейчас я действительно рассчитываю на то, что вы знакомы с I / G, иначе было бы трудно понять следующую функцию, которая широко известна как асинхронный запуск задач.

Существуют библиотеки, такие как Co, которые предлагают аналогичную функцию, предназначенную для запуска генераторов. Необязательно понимать функцию run, чтобы иметь возможность использовать ее; в C # есть «некоторая магия», которая заботится о том, как работает async / await, включая конечный автомат, и большинство из нас (разработчиков C #) успешно выполняли асинхронное программирование на C # не зная, как работает этот конечный автомат или почему он работает именно так. Но я всегда вам советую: исследуйте, изучайте и учитесь, чтобы вы могли понять, что вы используете, даже если это выглядит как черный ящик, который всегда даст вам преимущество и заставит вас чувствовать себя счастливым.

Теперь все, что нам нужно сделать, это:

Обратите внимание, как эта строка кода, содержащая yield, и следующая, использующая полученный результат, выглядят как синхронное программирование, без обратных вызовов, без цепочки then / catch, это просто похоже на C # async / await.

Объединение этой асинхронной модели выполнения задач с Promises даст вам еще больше возможностей и гибкости. Если ваши требования и среда позволяют вам, ECMAScript async / await, вероятно, будет лучше контролировать асинхронный поток; если вам не нужно заниматься более сложным сценарием, требующим гибкости и мощности I / G.

Лично я использую RxJS, когда это возможно, мне нравится реактивное программирование, о котором я рассказываю в своих статьях: https://itnext.io/functional-reactive-programming-explained-in-a-simple-way-in-javascript- yes-in-a-simple-way-925b14cddf75 и https://itnext.io/i-would-love-to-see-you-do-it-better-and-cleaner-without-reactive-programming- 545face12e1a , но я понимаю, что парадигма не всем нравится и может быть очень сложной. Итак, как я всегда советую (потому что я научился, как и вы, на собственном горьком опыте), выберите правильный инструмент для выполняемой работы и, в конце концов, почувствуйте себя счастливым и удовлетворенным своим решением.

Я прошу прощения за то, что не объяснил построчно код в этой статье, это было бы вне моей цели, которая заключалась в том, чтобы поделиться с теми, кто знаком с I / G (кто интересуется практическими вариантами использования), где и почему я их использовал .

Удачного кодирования!