Knockout.js предоставляет гибкий способ работы с динамическими данными в интерфейсе, однако для создания надежных приложений необходимо правильно обрабатывать ошибки и состояния загрузки. Основные механизмы для этого базируются на использовании observable, computed, а также на интеграции с асинхронными операциями.
Для отслеживания состояния загрузки данных создаются observable, которые сигнализируют о текущем статусе запроса. Обычно используют булевое значение:
function ViewModel() {
var self = this;
self.isLoading = ko.observable(false);
self.data = ko.observableArray([]);
self.loadData = function() {
self.isLoading(true);
fetch('/api/items')
.then(response => response.json())
.then(result => {
self.data(result);
self.isLoading(false);
})
.catch(error => {
console.error('Ошибка загрузки данных:', error);
self.isLoading(false);
});
};
}
ko.applyBindings(new ViewModel());
В этом примере isLoading переключается на
true перед началом асинхронного запроса и возвращается в
false после завершения или при ошибке. Это позволяет
привязать индикатор загрузки к элементам интерфейса через
visible binding:
<div data-bind="visible: isLoading">Загрузка...</div>
<ul data-bind="foreach: data">
<li data-bind="text: $data"></li>
</ul>
Ошибки запросов удобно хранить в отдельном observable, чтобы отображать их пользователю:
self.errorMessage = ko.observable('');
self.loadData = function() {
self.isLoading(true);
self.errorMessage('');
fetch('/api/items')
.then(response => {
if (!response.ok) throw new Error('Сервер вернул ошибку');
return response.json();
})
.then(result => {
self.data(result);
self.isLoading(false);
})
.catch(error => {
self.errorMessage(error.message);
self.isLoading(false);
});
};
Отображение ошибки в интерфейсе:
<div data-bind="visible: errorMessage, text: errorMessage" class="error"></div>
Такой подход позволяет отделить логику загрузки данных от логики отображения ошибок, делая код более модульным и поддерживаемым.
Иногда требуется объединить несколько состояний в одно вычисляемое значение. Например, определение, доступен ли контент для отображения:
self.hasData = ko.computed(function() {
return !self.isLoading() && self.data().length > 0 && !self.errorMessage();
});
Теперь hasData можно использовать для условного
отображения основного контента:
<div data-bind="visible: hasData">
<ul data-bind="foreach: data">
<li data-bind="text: $data"></li>
</ul>
</div>
Это упрощает управление интерфейсом и предотвращает конфликт отображения индикатора загрузки, ошибок и данных.
Knockout.js не ограничивает способ работы с асинхронностью.
Применение async/await делает код более читаемым:
self.loadData = async function() {
self.isLoading(true);
self.errorMessage('');
try {
const response = await fetch('/api/items');
if (!response.ok) throw new Error('Ошибка сервера');
const result = await response.json();
self.data(result);
} catch (error) {
self.errorMessage(error.message);
} finally {
self.isLoading(false);
}
};
Такой подход уменьшает вложенность then/catch и делает
обработку ошибок более предсказуемой.
Для больших приложений удобно создавать универсальные observables для статусов:
self.status = ko.observable({ loading: false, error: '' });
self.loadData = async function() {
self.status({ loading: true, error: '' });
try {
const response = await fetch('/api/items');
if (!response.ok) throw new Error('Ошибка сервера');
const result = await response.json();
self.data(result);
} catch (error) {
self.status({ loading: false, error: error.message });
} finally {
self.status({ ...self.status(), loading: false });
}
};
В интерфейсе:
<div data-bind="visible: status().loading">Загрузка...</div>
<div data-bind="visible: status().error, text: status().error" class="error"></div>
Такой шаблон позволяет легко масштабировать обработку ошибок и статусов для множества асинхронных операций.
finally или эквивалент.loading, error, success.Эти методы позволяют создавать реактивные и надежные приложения на Knockout.js с правильной обработкой ошибок и визуальной обратной связью при загрузке данных.