Абортирование валидации

В библиотеке Yup процесс валидации построен как последовательное применение цепочки правил к значению схемы. Несмотря на декларативный стиль описания схем, внутри движка существует несколько механизмов, влияющих на то, как и когда прекращается проверка данных. Одним из ключевых аспектов является управление прерыванием валидации, которое напрямую связано с поведением флага abortEarly.

В Yup стандартная функция проверки validate принимает опции, среди которых особое значение имеет параметр abortEarly. Он определяет стратегию обработки ошибок при валидации:

  • abortEarly: true — валидация прекращается при первой найденной ошибке
  • abortEarly: false — выполняется полная проверка всех правил и собираются все ошибки

Логика прерывания при abortEarly: true

При включённом режиме раннего прерывания Yup ведёт себя как последовательный фильтр: каждое правило проверяется по очереди, и при обнаружении первого несоответствия дальнейшая проверка прекращается. Это касается как встроенных валидаторов (required, min, max, matches), так и пользовательских тестов, определённых через test.

Пример поведения:

  • Проверка поля начинается
  • Выполняется первое правило
  • При ошибке создаётся ValidationError
  • Дальнейшие правила не выполняются

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

Полная валидация при abortEarly: false

Когда abortEarly отключён, Yup переходит к агрегационной модели. Все правила для каждого поля выполняются независимо от уже найденных ошибок.

Результатом становится объект ValidationError, содержащий массив inner, в котором перечислены все найденные нарушения. Каждый элемент массива описывает отдельную ошибку с указанием:

  • пути (path) к полю
  • типа ошибки (type)
  • сообщения (message)
  • исходного значения (value)

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

Структура ValidationError и накопление ошибок

Механизм прерывания тесно связан с тем, как Yup формирует структуру ошибок. При отключённом abortEarly ошибки не выбрасываются немедленно, а накапливаются внутри объекта ValidationError.

Ключевые элементы:

  • message — агрегированное сообщение (часто первое по порядку)
  • errors — массив текстовых сообщений
  • inner — массив детализированных ошибок
  • path — путь до поля в объекте
  • value — значение, вызвавшее ошибку

При этом даже если одно поле содержит несколько нарушений, они фиксируются отдельно, если это допускается структурой схемы (например, вложенные test или массивы условий).

Abort в пользовательских тестах

Функция test в Yup позволяет добавлять кастомные проверки. Внутри неё также возможно управление логикой прерывания, хотя оно реализуется неявно.

Yup.string().test('custom-check', function (value) {
  if (!value) return true;
  if (value.length < 5) {
    return this.createError({ message: 'Слишком короткое значение' });
  }
  return true;
});

Когда используется createError, текущий тест считается проваленным, и дальнейшие тесты для этого уровня схемы могут быть остановлены в зависимости от abortEarly.

Важно, что внутри одного test нет встроенного механизма “abort chain” — прерывание контролируется на уровне всей схемы, а не отдельной функции.

Влияние abortEarly на вложенные схемы

Yup поддерживает сложные вложенные структуры: объекты, массивы, комбинации схем. Поведение abortEarly распространяется рекурсивно.

Объекты

При проверке объекта:

  • каждый ключ валидируется независимо
  • при abortEarly: true ошибка в первом невалидном поле может остановить проверку остальных полей объекта

Массивы

Для массивов применяется дополнительный уровень итерации:

  • каждый элемент массива проходит проверку по схеме
  • внутри элемента действует свой контекст abortEarly

При abortEarly: false возможна ситуация, когда один элемент массива содержит несколько ошибок, и все они фиксируются отдельно.

Стратегии прерывания валидации

В реальных сценариях используются различные стратегии управления остановкой проверки.

Раннее прерывание (fail-fast)

Используется при:

  • проверке API входных данных
  • валидации критических параметров
  • снижении нагрузки на сложные схемы

Преимущество — минимальное время выполнения. Недостаток — отсутствие полной картины ошибок.

Полная агрегация ошибок

Используется при:

  • формах пользовательского ввода
  • UI-валидации
  • системах с множественными полями

Позволяет собрать полный список ошибок и отобразить их одновременно.

Асинхронная валидация и прерывание цепочек

Yup поддерживает асинхронные проверки, включая запросы к серверу через test с async функцией.

Yup.string().test('check-remote', async function (value) {
  const exists = await apiCheck(value);
  if (!exists) {
    return this.createError({ message: 'Значение не существует' });
  }
  return true;
});

В асинхронных схемах прерывание работает аналогично синхронным:

  • при abortEarly: true промис отклоняется сразу после первой ошибки
  • при abortEarly: false все проверки дожидаются завершения

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

Отсутствие нативного AbortController

Несмотря на терминологическое сходство с концепцией “abort”, Yup не использует AbortController или сигналы отмены. Прерывание реализовано исключительно на уровне логики выполнения цепочки валидаторов.

Это означает:

  • невозможно принудительно остановить уже запущенный async test
  • нет механизма внешнего “cancel validation”
  • контроль осуществляется только через abortEarly

Оптимизация через ранний выход

Механизм прерывания часто используется как способ оптимизации сложных схем.

Примеры оптимизационных подходов:

  • размещение дешёвых проверок (например, required) перед дорогими (test, async вызовы)
  • использование when для условного включения валидаторов
  • разделение схем на независимые части

Пример условного прерывания:

Yup.string().when('type', {
  is: 'email',
  then: schema => schema.email().required(),
  otherwise: schema => schema.required()
});

Хотя when не является механизмом abort, он позволяет уменьшить количество выполняемых проверок, тем самым косвенно влияя на точки остановки.

Влияние abortEarly на производительность

При больших схемах различие между режимами становится заметным:

  • abortEarly: true снижает количество выполняемых тестов
  • abortEarly: false увеличивает нагрузку за счёт полного обхода дерева схем

Особенно это критично для:

  • массивов с большим количеством элементов
  • вложенных объектов с повторяющимися правилами
  • схем с async-валидацией

Поведение при комбинации нескольких схем

При использовании concat, shape и комбинированных схем порядок выполнения влияет на момент прерывания.

Yup выполняет проверку в порядке объявления схемы, поэтому:

  • первая ошибка в первой схеме может полностью остановить дальнейшую проверку
  • при отключённом abortEarly порядок влияет только на порядок ошибок в inner

Роль abortEarly в архитектуре валидации

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

Внутренне это выражается через:

  • прекращение итерации по тестам
  • отказ от дальнейшего вызова валидаторов текущего уровня
  • возврат исключения ValidationError или его накопление

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