Автор оригинала: FreeCodeCamp Community Member.
Async Generators – это простая, но мощная функция, которая сейчас является частью JavaScript. Он разблокирует многие новые инструменты для улучшения архитектуры программного обеспечения, что делает его более гибким, упрощенным расширением и составом.
TL; доктор
- С Async Generators больше не нуждаются в состоянии компонентов, государственных инструментов управления, методам жизненного цикла компонентов и даже последним контекстом React, крючков и APIC. Это намного проще развиваться, поддерживать и тестировать.
- В отличие от государственного подхода к управлению, асинхронистыми генераторами ручной асинхроничности, оставляя мутации безвредны (если видны только в объеме генератора).
- Этот подход имеет функциональный программирующий фон.
- Государственная настойчивость для вещей, таких как время путешествия, универсальные приложения также доступны.
- В статье используются React и JavaScript, но методика применима в любых других рамках или языке программирования с генераторами (COROUTINES).
- Я рекламирую свой инструмент только в конце и очень кратко. Большая часть статьи о асинхронных генераторах без какой-либо зависимости.
Начнем с заявления от Redux Motiveration Page :
Redux и другие государственные инструменты управления в основном сосредоточены на ограничении или управлении мутациями данных. Асинхронные генераторы могут обрабатывать асинхронность. Это делает мутацию в безопасности, если это видно только в пределах определенного объема генератора.
Все общие методы государственного управления могут быть разделены на два больших класса.
Первый класс поддерживает график зависимостей данных для распространения изменений через обработчики – Complete Component, MOBX, RXJS. Поддержание этих зависимостей является сложная задача. Основные библиотеки предпринимают ответственность за частью этой сложности, управляя подпискими, оптимизирующими порядок выполнения обработчиков, добивая их, но он по-прежнему запутается иногда, часто требует жесткой тонкой настройки, например, с должен быть необходим
метод.
Другой подход ограничивает мутацию только к одной ячейке (хранилище) (например, redux). Это нужны гораздо меньшие библиотеки, с меньшими магией в них. Это больше шаблона, чем библиотека. К сожалению, программы более многословны, и это разрывает инкапсуляцию данных. Есть много шаблонов, обобщиков, чтобы решить это, но они делают один сотовый подход, чтобы быть более похожим на график, основанный на одном.
Техника в этой истории и Redux основана на шаблоне источника событий, и у них много сходств. Он также предлагает инкапсулированные данные и синхронный детерминированный порядок выполнения для операций с побочными эффектами.
Этот подход может быть абстрактно рассматривается как график зависимости, но изменения распространяются в обратном направлении, от его корня к листьям его охватывающего дерева. В каждом узле мы проверяем, если распространение приступает к детям или нет. Это делает алгоритм планирования очень легким и легко контролировать. Это не требует никакой библиотеки, основывая только на встроенных функциях JavaScript.
Давайте первый порт Redux VanillaJs счетчики Пример, чтобы проиллюстрировать идею.
Оригинальный редуктор заменяется функцией Async Generator. Функция рассчитывает и хранит свое состояние в локальной переменной. Он также дает рассчитанное значение, новое значение хранится в хранилище синглтона, и он виден из обработчиков событий. Я удалю этот синглтон хранилище на следующих шагах.
Эта версия не выглядит сильно отличаться от Redux. Асинхронный генератор может быть промежуточное программное обеспечение для хранения Redux. Это нарушает один из redux Принципы Хотя, а именно хранение всего состояния приложения только в хранилище. Даже если генератор не имеет локальных переменных, у него все еще есть состояние выполнения – положение в коде, где выполнение приостановлено в доходность
или ждать
Отказ
Поворот компонентов наизнанку
Функции генератора являются функциями, возвращающими итераторы. Мы можем сделать с ними все, что мы можем сделать с простой функциями. Например, составляя функции генератора, мы можем разделить вычисление на несколько независимых стадий. Каждый этап получает сообщения, которые были уступлены на предыдущем этапе, обрабатывает их, дает еще один обмен сообщениями и передает их на следующий этап.
Полезная нагрузка сообщений может содержать элементы VDOM. Вместо того, чтобы иметь монолитное дерево компонентов, мы излучаем его части и отправляем их на следующий этап, где они могут быть собраны или трансформированы. Вот те же пример приложения с реакцией.
Там труба
Функция – это функциональная композиция. Функции принимают два аргумента. Первый асинхронизирован для сообщений с прежнего этапа. А второй – отправить сообщение в начало трубы. Его следует вызывать только из обработчиков событий. Эта функция может быть заменена в ближайшее время с оператором встроенного трубопровода JavaScript.
Когда мы составляем простые функции, следующая в цепочке начинается выполнение только после завершения предыдущего. В то время как для генераторов (и на самом деле любые CITOROUTINES) выполнение может быть приостановлено в переплетении с другими функциями. Это облегчает сочинение разных частей.
Пример выше кратко показывает расширяемость путем развязки нескольких кнопок меню из корневого компонента в отдельную стадию. Вместо того, чтобы абстрагировать кнопки меню в отдельный компонент, он поддерживает заполнитель, где он впрыскивает компоненты, которые он получает в сообщениях с типом «MENU_ITEM». Это инверсия управления для компонентов. Обе методы реагируют компоненты, и эти перевернутые компоненты могут быть использованы вместе.
Расширение
Увлекательная точка этого метода ничто не должно быть предварительно разработана, чтобы сделать программу многоразовой и развязанной. В настоящее время преждевременная абстракция, вероятно, является большем злом, чем преждевременная оптимизация. Это почти определенно приводит к переношенному беспорядку, невозможно использовать. Использование абстрактных генераторов, легко сохранить спокойствие и реализовать необходимые функции, разделенные при необходимости, не думая о будущих расширениях, легко рефакторируйте или абстрактные общие детали после получения дополнительной доступности.
Redux известен своими программами проще продлить и повторно использовать. Подход в этой истории также основан на источнике событий, но намного проще запустить асинхронные операции, и у него нет единого узкого места в магазине, ничего не должно быть спровоцировано преждевременно.
Многие разработчики любят одно хранилище, потому что это легко контролировать. Контроль не является свободным, хотя. Одним из широко принятых преимуществ структуры источника событий является отсутствие центральной БД. Проще всего изменить одну часть без опасности нарушения чего-то другого. Существует еще одна проблема однократного хранения, обсуждаемая в разделе «Настойчивость» ниже.
Есть Декупла бизнес логика Статья с более подробными случаями исследования. На некоторых шагах я добавил функцию Multi-Select для перетаскивания, не меняя ничего из-за одного элемента. С помощью одного магазина это означало бы изменение своей модели от хранения одного элемента перетаскивания в данный момент в список.
Существуют подобные решения в Redux, а именно применяя редуктор более высокого порядка. Это может взять редуктор, работающий с одним элементом и переводится в редуктор, работающий в списке. Раствор генераторов использует Async-генераторы более высокого порядка, принимая функцию для одного элемента и генерируя один для списка. Это похоже, но гораздо меньше многословного, поскольку генератор инкапсулирует данные и неявное контрольное состояние.
Как иллюстрация, давайте сделаем список счетчиков. Этот шаг покрыт статьей «Декупла Business Logic», я не даю много подробностей здесь. вилка
Функция – это функция преобразования асинхронизации итераторы, выполняя его аргумент в потоках на предмет. Это не просто, но это универсальный, работает во многих контекстах, как есть. Например, в следующем разделе я прилагаю его рекурсивно, чтобы получить вид дерева.
Представление
Async Generators накладные расходы намного меньше, чем для государственных библиотек управления. Но есть много способов получить проблемы с производительностью здесь, например, над затоплением сообщений. Но также есть много практически легких способов повышения производительности.
В первом примере есть бесполезные звонки на Reactom.runder
Отказ Это, очевидно, проблема с производительностью, и есть простое решение. Решение его быстро, отправив другое сообщение с помощью типа «Flush» после каждого отправленного события. Реагистрирование рендеринга работает только после получения этого сообщения. Промежуточные шаги могут дать то, что им нужно между ними.
Еще одна удивительная сторона этого подхода, вы можете не беспокоиться о производительности, пока это не проблема. Все структурировано на небольших автономных этапах. Они легко рефакторируют, или даже без рефакторинга – многие проблемы с производительностью могут быть решены путем добавления другого общего состояния в трубе этапов, например, дозирования, приоритеты к приоритетам, сохранению промежуточных данных и т. Д.
Например, в демонстрационных реакцию элементы реагируют в локальных переменных и реагируют повторно использовать их. Изменения распространяются от корня к листьям, поэтому оптимизации, такие как переопределение должен быть необходим
не нужно.
Тестирование
По сравнению с редукторами Redux Generators подходит немного более темной стратегией тестирования коробки. Тесты не имеют доступа к текущему состоянию. Хотя все еще они очень просто писать. С шумами шума, тест может представлять собой список входных сообщений со сравнением вывода с помощью снимков.
test("counterControl", async () => { expect.assertions(3) for await(const i of Counter.mainControl([ {type:"MENU", value:Menu}, {type:"VALUE", value:10}, {type:"CONTROL", value:Control}, {type:"FLUSH"}, {type:"VALUE", value: 11}, {type:"FLUSH"}])) if (i.type === "CONTROL") expect(renderer.create(i.value).toJSON()).toMatchSnapshot() })
Если вы предпочитаете тесты подразделения как политику документации, существует много способов сделать самодокуменную API для тестирования. Скажем, функция «в конечном итоге»/`пока` в качестве дополнения к традиционным выражениям BDD.
Постоянное состояние
Есть еще одна мотивация для redux, описанная в Возможно, вам не понадобится redux Статья Дэна Абрамова – а именно предоставление доступа к государству и может быть сериализовано, клонированным, распространенным, исправленным и т. Д. Это может быть использовано для прохождения времени, горячей перезагрузки, универсальных приложений и многого другого.
Для этого на работе все состояние приложения должно храниться в хранении Redux. Многие приложения Redux (даже образцы Redux) имеют некоторую часть состояния, хранящейся за пределами своего магазина. Это компоненты состояния, закрытия, генераторы или состояние асинхронизации. Редукс на основе инструментов не может упорствовать это состояние.
Наличие единого источника истины как единого хранилища Redux, конечно, делает программы проще. К сожалению, это часто невозможно. Рассмотрим, например, распространенное приложение, например, данные передаются между Frontend и Backend.
«О, ты хотел * увеличивать счетчик *?! Удачи с этим!» – распределенные системы литературы
Поиск событий очень успешно для распределенных приложений. С генераторами мы можем написать прокси-сервер, отправляя все входящие сообщения на удаленную сторону и давая все полученные сообщения. Могут быть отдельные трубопроводы на каждом одноранговременте, или это может быть одинаковое приложение, но и несколько рабочих процессов. Многие конфигурации легко настроить, использовать и повторно использовать.
Например Труба (Task1, Remotetask2, Task3)
Отказ Здесь Remotetask2
Может быть либо прокси, либо он может быть определен здесь, скажем, для целей отладки.
Каждая часть поддерживает свое собственное состояние, его не нужно постоянно. Скажем, если каждая задача реализована отдельной командой, они свободно использовать любую модель для состояния, измените ее в любое время, не беспокоясь о работе другой команды нарушены.
Это хорошо подходит для рендеринга боковых серверов. Скажем, может быть определенный функция более высокого порядка для кэширования полученных значений в зависимости от входных данных на заднем дне.
const backend = pipe( commonTask1, memo(pipe( renderTask1, renderTask2)), commonTask2)
Здесь Memo
Функция более высокого порядка осматривает входящие сообщения и может выяснить, что некоторые расчеты могут быть повторно использованы. Это может представлять собой строку визуализации на стороне сервера, а некоторые следующие этапы строят ответ HTTP.
Задачи Render могут запускать Async-операции, запрашивая что-то удаленное. Для лучшего пользовательского опыта мы хотим, чтобы страницы быстро загружались. Чтобы увеличить начальные приложения времени загрузки страницы могут загружать компоненты лениво, отображающие некоторую загрузку заполнителя вместо компонента, пока он не будет готов. Имея несколько таких компонентов на странице с немного разным временем загрузки, вызывает страницу переназначивает ухудшение опыта пользователя.
Реагистрационная команда недавно объявила о неизвестности API для решения этой проблемы. Это расширение реагирования встроена в его рендерер. С помощью инвертированных компонентов, таких как в этой статье, API ожидания не требуется, решение намного проще, а не частью пользовательского интерфейса.
Скажем, приложение использует динамический импорт для загрузки ленивых элементов управления, это можно сделать с:
yield {type:"LAZY_CONTROL"} yield {type:"CONTROL", value: await import("./lazy_component")}
Есть еще один универсальный следующий этап. Он собирает все сообщения «LZO_CONTROL», а ждет либо все «контрольные» сообщения получены после или порогового интервала времени. После того, как он излучает «контроль» сообщения либо с загруженным управлением, либо с заполнителем индикатора нагрузки. Все следующие обновления могут быть нанесены, используя некоторые определенные тайм-аут, чтобы минимизировать переработки.
Некоторые генератор также могут перенастроить сообщения, чтобы дать анимацию более широкий приоритет, чем обновления данных сервера. Я даже не уверен, что есть потребности в серверной основе. Крошечный генератор может преобразовать начальный HTTP-запрос в сообщения или потоки в зависимости от сеанса URL, AUTH и т. Д.
Функциональное программирование
Обычно используемые стационарные инструменты управления имеют фон FP. Код из статьи не похож на FP в JavaScript из-за императивных Для/переключатель/перерыв
заявления. Он также имеет соответствующую концепцию в FP. Это так называемые монады. Например, одно из их использовании в Haskell – решать проблемы, такие как компоненты реагирования.
Чтобы сохранить эту историю практичной, я не отступаю с главной темой здесь, есть другая статья – Использование генераторов как синтаксис сахара для побочных эффектов Отказ
Эффективность
Creatual.js Предварительная реализация Babel Repainting Do-OneOtation работает на любой монаде без какого-либо расширения синтаксиса JavaScript. Это также поддерживает государственную стойкость со ссыльной реализацией в es-persist библиотека. Например, это может использоваться для преобразования всех примеров Async Generators выше в чистые функции.
Государственная настойчивость не является основной целью инструмента. Это для более высокого уровня бизнес-логики. Тем не менее, инструмент абстракция и имеет много целей. Я напишу о них больше.
Вот Сводка выборки На Github со всей функцией выше плюс автоматическая отмена/повторение и хранение его полного состояния в LocalStorage
Отказ А вот работает транспортировано Версия (она записывает на ваши браузеры локальное хранилище, но информация не отправляется на стороне сервера). Я не даю много деталей в этой статье, речь идет о асинхронных генераторах без зависимости, но я предполагаю, что код просто прочитан. Проверьте, например undoredo.js Для легкого времени путешествуя детали реализации.
Оригинальный образец требует практически никаких изменений, я только заменил не сериализуемые обещания, причем соответствующие функции «ES-Spaist» и заменены закрытыми с вызовами R.Bind
Функция из той же библиотеки. EffectFuljs Toolchain имеет еще один транспортер, чтобы сделать все функции, включая замыкание сериализуемых, но не используемых в этом примере, чтобы сохранить его проще.
История – это просто краткое описание техники. Я уже использую его уже пару лет, и счастлив из-за улучшений. Попробуйте, и я уверен, что вам тоже понравится. Есть много вещей, которые нужно описать в глубине. Следите за обновлениями!