- PVSM.RU - https://www.pvsm.ru -
Сегодня мало кто помнит, что веб-приложения могут работать без единого XHR-запроса. AJAX (Asynchronous Javascript and XML) дает классную возможность — подгружать данные без перезагрузки страницы. Эта концепция лежит в основе большинства современных SPA.
Но ничто не дается просто так, за все нужно платить. Концепция AJAX кажется предельно простой, но даже на уровне запроса данных с сервера можно встретить кучу проблем.
Для начала давайте напишем самое простое SPA-приложение с AJAX:
initApp();
function initApp() {
document.body.innerHTML = `
<h3>Employees list</h3>
<ul id="employees-list"></ul>
<button id="load-employees">Load employee</button>
`;
document.getElementById('load-employees').addEventListener('click', loadEmployee);
}
function loadEmployee() {
fetch('http://dummy.restapiexample.com/api/v1/employee/1')
.then(res => res.json()).then(({employee_name}) => addEmployee(employee_name));
}
function addEmployee(employeeName) {
const employeeElement = document.createElement('li');
employeeElement.innerText = employeeName;
document.getElementById('employees-list').appendChild(employeeElement);
}
Все предельно просто: при клике на кнопку запрашиваем данные с сервера и при их получении добавляем элемент в список.
Как я говорил, на этом этапе многое может пойти не по плану и, чтобы раскрыть тему глубже, сначала разберем немного теории.
Это философия проектирования интерфейсов, при которой пользователю изначально предоставляется максимально возможное количество функций. И только в случае отказа какой-либо из частей системы отключаются зависимые от нее функции. Звучит сложно, но ниже мы разберем это на примере — так будет гораздо понятнее.
Существует альтернативная/параллельная философия — progressive enhancement. В ней движение идет в другую сторону: изначально пользователю предоставляется минимальный (либо средний) набор функциональностей. А для инициализации остальных сначала проверяется поддержка необходимых для их работы частей системы.
Обычно, когда говорят про graceful degradation и progressive enhancement в контексте браузерных приложений, имеют в виду кроссбраузерность или адаптивность. Есть популярный пример, объясняющий эти концепции. Допустим, в вашем приложении есть функция печати страницы, и если вы делаете так:
<body>
<a href="javascript:window.print()" id="print-btn">Print</a>
<script>
const printButton = document.getElementById('print-btn');
if (printButton && typeof window.print !== 'function') {
printButton.parentNode.appendChild(document.createTextNode('Печать не поддерживается'));
printButton.parentNode.removeChild(printButton);
}
</script>
</body>
то это graceful degradation, потому что вы сразу показываете кнопку печати, но когда понимаете, что печать не поддерживается браузером, то удаляете функциональность.
P.S.: В оригинальном примере при демонстрации graceful degradation использовался тег noscript, но, мне кажется, это сильно устарело.
Если же вы делаете так:
if(typeof window.print === 'function') {
const printButton = document.createElement('a');
printButton.innerText = 'Print';
printButton.addEventListener('click', () => window.print());
document.body.appendChild(printButton);
}
это уже progressive enhancement, потому что сначала вы проверяете поддержку необходимого API и только потом добавляете функцию.
Примеры демонстрируют самое примитивное применение философий построения отказоустойчивых интерфейсов.
Вернемся к AJAX и HTTP-запросам.
Самым простым случаем будет, если сервер вернет не тот статус-код, что вы ожидали, допустим 500. Это частый сценарий, и вы наверняка имеете некоторые инструменты для его обработки. Например, показываете пользователю уведомление «Произошла ошибка сервера». Это явно degradation, но на сколько он graceful? А можно ли тут применить progressive enhancement? Нет, это точно не место для progressive enhancement — функциональность уже деградировала. Можно только красиво обыграть эту неприятность:
Никогда не доверяйте бэкэнду! Сервер может ответить кодом 200, но в теле ответа вернет не те данные, которые вам нужны. В этой ситуации можно делать то же самое, что и при неожиданном статус-коде, но сложность в том, чтобы определить, что ответ действительно невалидный.
Если вы пишите на typescript, то для вас есть классный инструмент — typescript-json-schema [2]. С его помощью можно генерировать json-схемы из интерфейсов typescript и использовать их для валидации данных в runtime.
Это тот удар, которого мало кто ожидает. Если об ошибках или даже о невалидных данных ответа мы помним, то про тайм-ауты вспоминаем редко. Виновником ситуации может быть не только серверное приложение, но даже провайдер интернета или устройство клиента.
Не стоит об этом забывать, лучше уведомить пользователя, что запрос выполняется дольше обычного, чем оставить его один на один с крутящимся кружком на экране. При истечении времени, выделенного на выполнение запроса, можно проходить аналогичный сценарий, что и в двух предыдущих ситуациях.
Я был сильно впечатлен, узнав, что у «Гугл-документов» есть офлайн-режим. Это очень помогло мне, когда я решил дописать статью в самолете, где не было интернета.
Конечно, приложения бывают разные и многие из них практически бесполезны без интернета. Но даже в этих приложениях можно обрабатывать случай с отсутствием соединения и показывать информативное сообщение (хотя игра в тираннозавра в «Хроме» мне тоже нравится).
Кроме этого вы можете слушать события подключения/отключения интернет-соединения [3]. И, например, автоматически перезагружать данные при событии online на window.
Итого, список действий, которые необходимо реализовать при вызове HTTP-запроса:
То, что сначала казалось тривиальным, вылилось в целую философию с множеством проблем. Конечно, это не мастхэв. Но если ваше приложение достигло высокого уровня зрелости и вы хотите сделать действительно качественный интерфейс, то это то направление, в котором стоит развиваться.
Цель этой статьи — рассказать о возможных проблемах при работе с HTTP-запросами, но не о конкретных решениях. Сегодня существует большое количество библиотек и фреймворков, которые нацелены на решение этих проблем, например, HTTP interceptors в Angular.
Зная о возможных проблемах, будет гораздо легче найти для них решение в интернете.
Автор: Alexandr Kazachenko
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/343025
Ссылки в тексте:
[1] sentry.io: https://sentry.io/
[2] typescript-json-schema: https://github.com/YousefED/typescript-json-schema
[3] события подключения/отключения интернет-соединения: https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/Online_and_offline_events
[4] Источник: https://habr.com/ru/post/483756/?utm_source=habrahabr&utm_medium=rss&utm_campaign=483756
Нажмите здесь для печати.