Обработка ошибок сети

Backbone.js строит взаимодействие с сервером вокруг моделей (Backbone.Model), коллекций (Backbone.Collection) и метода Backbone.sync, который по умолчанию использует jQuery.ajax. Сетевые ошибки возникают на нескольких уровнях:

  • ошибки транспортного уровня (нет соединения, таймаут, DNS);
  • HTTP-ошибки (4xx, 5xx);
  • ошибки формата ответа (некорректный JSON);
  • логические ошибки API (валидный HTTP-ответ с ошибкой в теле).

Корректная обработка требует понимания, на каком этапе произошёл сбой, и как Backbone передаёт информацию об ошибке.


Механизм Backbone.sync и распространение ошибок

Все операции fetch, save, destroy в конечном итоге вызывают Backbone.sync(method, model, options). Если сервер возвращает ошибку, Backbone:

  • не обновляет атрибуты модели;
  • вызывает callback options.error;
  • триггерит событие error у модели или коллекции.

Сигнатура error-обработчика:

error: function(model, xhr, options) {
    // model — модель или коллекция
    // xhr — jqXHR-объект
    // options — объект параметров
}

Ключевые данные содержатся в xhr:

  • xhr.status — HTTP-код;
  • xhr.responseText — текст ответа;
  • xhr.responseJSON — распарсенный JSON (если доступен).

Обработка ошибок при fetch

fetch используется для загрузки данных с сервера и особенно чувствителен к сетевым сбоям.

model.fetch({
    success: function(model, response) {
        // данные успешно загружены
    },
    error: function(model, xhr) {
        if (xhr.status === 404) {
            // ресурс не найден
        }
    }
});

При ошибке:

  • атрибуты модели не изменяются;
  • событие sync не вызывается;
  • вызывается событие error.

Дополнительно можно подписаться на событие:

model.on('error', function(model, xhr) {
    // централизованная реакция
});

Обработка ошибок при save

save может выполняться как POST, PUT или PATCH. Ошибки здесь критичны, так как связаны с сохранением состояния.

model.save(data, {
    wait: true,
    error: function(model, xhr) {
        // сервер отклонил сохранение
    }
});

Параметр wait: true Если не указан, Backbone оптимистично обновляет модель до ответа сервера. При сетевой ошибке состояние уже будет изменено, что усложняет откат.

С wait: true:

  • изменения применяются только после успешного ответа;
  • при ошибке модель остаётся в прежнем состоянии.

Серверные ошибки в теле ответа

Часто API возвращает HTTP 200, но сообщает об ошибке в JSON:

{
  "error": "Validation failed",
  "fields": {
    "email": "invalid"
  }
}

Backbone не считает такой ответ ошибкой автоматически. Необходима ручная проверка в success:

success: function(model, response) {
    if (response.error) {
        model.trigger('error', model, response);
        return;
    }
}

Для системной обработки логических ошибок этот подход неудобен. Более правильное решение — использовать validate.


Использование validate для сетевых ошибок

Метод validate(attrs, options) вызывается перед сохранением, но может применяться и для анализа серверного ответа:

validate: function(attrs, options) {
    if (options.serverError) {
        return options.serverError;
    }
}

В error-callback:

error: function(model, xhr) {
    var response = xhr.responseJSON;
    model.save(null, {
        serverError: response
    });
}

Если validate возвращает значение, Backbone:

  • отменяет обновление;
  • триггерит событие invalid.

Централизация обработки ошибок через переопределение Backbone.sync

Для крупных приложений локальная обработка быстро приводит к дублированию кода. Backbone допускает глобальное переопределение:

var originalSync = Backbone.sync;

Backbone.sync = function(method, model, options) {
    var error = options.error;

    options.error = function(xhr) {
        if (xhr.status === 401) {
            // неавторизован
        }
        if (error) error.apply(this, arguments);
    };

    return originalSync.call(this, method, model, options);
};

Преимущества:

  • единая логика обработки HTTP-кодов;
  • централизованное логирование;
  • единообразная реакция на сетевые сбои.

Таймауты и повторные запросы

По умолчанию jQuery.ajax не задаёт таймаут. Его можно определить в sync или в конкретном запросе:

model.fetch({
    timeout: 5000,
    error: function(model, xhr) {
        if (xhr.status === 0) {
            // вероятный таймаут или отсутствие сети
        }
    }
});

Реализация повторных попыток:

function fetchWithRetry(model, retries) {
    model.fetch({
        error: function() {
            if (retries > 0) {
                fetchWithRetry(model, retries - 1);
            }
        }
    });
}

Ошибки коллекций

Коллекции обрабатывают ошибки аналогично моделям, но важно учитывать, что ошибка может относиться ко всему набору данных.

collection.fetch({
    error: function(collection, xhr) {
        // коллекция осталась в прежнем состоянии
    }
});

Событие:

collection.on('error', function(collection, xhr) {
    // обработка
});

При частичном успехе (сервер вернул неполный список) Backbone не предоставляет встроенных механизмов различения — ответственность лежит на API.


Парсинг ответа и ошибки формата

Метод parse(response, options) вызывается при успешном HTTP-ответе. Если внутри parse происходит ошибка (например, неожиданная структура данных), это не считается сетевой ошибкой, но может привести к неконсистентному состоянию.

Рекомендуется явно проверять формат:

parse: function(response) {
    if (!Array.isArray(response.items)) {
        throw new Error('Invalid response format');
    }
    return response.items;
}

Такие ошибки лучше перехватывать на уровне sync или глобального обработчика исключений.


Разделение сетевых и доменных ошибок

Практика показывает необходимость различать:

  • ошибки соединения;
  • ошибки протокола;
  • ошибки бизнес-логики.

Типовая схема:

  • HTTP-коды → сетевой слой;
  • JSON с ошибками → слой моделей;
  • события (error, invalid) → слой представлений.

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


Ключевые моменты

  • Backbone не интерпретирует тело ответа — только HTTP-статус.
  • options.error и событие error — основные точки входа.
  • wait: true критичен для корректной работы при сбоях.
  • Переопределение Backbone.sync позволяет реализовать глобальную стратегию.
  • Логические ошибки API требуют дополнительного слоя обработки.

Такой подход обеспечивает предсказуемое поведение приложения при нестабильной сети и упрощает сопровождение кода в долгосрочной перспективе.