Библиотека Yup поддерживает асинхронные проверки через
пользовательские тесты, возвращающие Promise. Такая модель
позволяет интегрировать серверные проверки, запросы к API и любые
операции, требующие времени выполнения вне синхронного контекста.
Асинхронная логика в Yup реализуется через test, где
функция может возвращать промис. Результат промиса определяет успешность
или провал проверки. На уровне API это выглядит как расширение
синхронной схемы, однако поведение внутри цепочки валидации существенно
отличается.
const schema = yup.object({
email: yup.string().test(
'unique-email',
'Email уже используется',
async (value) => {
const res = await fetch(`/api/check-email?value=${value}`);
const data = await res.json();
return data.unique;
}
)
});
Такая модель кажется прямолинейной, однако именно здесь проявляется большинство сложностей.
Ключевая проблема асинхронной валидации заключается в отсутствии механизма отмены предыдущих запросов. При каждом изменении значения поля запускается новая проверка, однако предыдущие промисы продолжают выполняться до завершения.
Это приводит к ситуации, когда более поздний ввод может быть перекрыт результатом устаревшего запроса.
Сценарий:
a@example.comb@example.comТак формируется классическая проблема гонки состояний (race condition).
Гонка возникает не только при проверке уникальности. Любая асинхронная логика, зависящая от внешнего источника, подвержена этому эффекту:
Поскольку Yup не хранит контекст “актуального запроса”, результат последнего завершившегося промиса считается валидным, даже если он относится к устаревшему состоянию формы.
Типичная ошибка проявляется в виде “мерцающих” ошибок: поле сначала становится валидным, затем снова невалидным без изменения значения.
Асинхронная валидация в Yup запускается каждый раз при вызове
validate или validateAt. В отличие от
UI-уровня, библиотека не управляет частотой вызовов.
При вводе текста в реальном времени это приводит к множеству параллельных запросов:
PromiseДебаунс обычно реализуется вне Yup, на уровне формы или обработчика событий, однако отсутствие встроенного механизма приводит к фрагментации логики.
При использовании Yup в связке с библиотеками управления формами, например Formik или React Hook Form, асинхронные ошибки начинают проявляться через сложные цепочки обновлений состояния.
Основные проблемы:
setFieldValuevalidateFieldisValidВ результате состояние формы перестаёт быть детерминированным в момент активного ввода.
Методы validate, validateAt и
isValid имеют различную семантику обработки
асинхронности.
validate возвращает полную структуру ошибок или
undefinedvalidateAt проверяет отдельное полеisValid возвращает булево значениеПроблема заключается в том, что isValid может возвращать
устаревший результат при параллельных асинхронных тестах. Это связано с
тем, что состояние не синхронизируется с завершением всех промисов.
Асинхронные тесты могут завершаться в произвольном порядке. Это создаёт ситуацию, когда:
Yup не хранит идентификаторы запросов, поэтому нет механизма сопоставления результата с конкретной версией значения поля.
Конструкции вида .when добавляют дополнительный слой
сложности. При изменении одного поля запускается пересчёт валидаторов
другого поля, включая асинхронные тесты.
Это создаёт каскад:
В условиях высокой частоты изменений формируется лавинообразный поток промисов.
Асинхронная валидация в Yup не имеет встроенного слоя управления состоянием запросов. Это означает:
Каждый тест выполняется изолированно, что упрощает API, но усложняет практическое поведение в UI.
Частичная стабилизация поведения достигается через ручное кэширование результатов проверки.
Например, проверка уникальности может использовать локальный кеш:
Однако такой подход имеет ограничения:
Yup не предоставляет встроенного слоя кеширования, поэтому логика переносится в пользовательские тесты.
При вызове полной валидации схемы (schema.validate)
происходит одновременный запуск всех асинхронных тестов. Если несколько
полей содержат async-логику, формируется набор параллельных
промисов.
Сложность возникает в следующих аспектах:
При больших формах это может приводить к значительным задержкам.
Повторные вызовы валидации часто происходят при каждом изменении состояния формы. При асинхронной логике это формирует цепочки незавершённых промисов.
Типичные последствия:
Особенно заметно при медленных API или нестабильном соединении.
Асинхронные тесты Yup выполняются последовательно внутри одного поля, но не управляются внешним контекстом отмены. Даже если значение поля изменилось, ранее запущенная цепочка продолжает выполняться до завершения.
Это создаёт фундаментальное ограничение архитектуры: логика валидации отделена от жизненного цикла UI-компонента.
При увеличении количества асинхронных проверок в схеме возникают следующие эффекты:
Особенно критично это проявляется в сложных формах с зависимыми полями и серверной валидацией.
Асинхронная валидация в Yup формирует набор системных ограничений:
Эти особенности делают асинхронные тесты мощным, но требовательным инструментом, требующим внешней архитектурной поддержки для стабильного поведения в реальных интерфейсах.