Представим, что ваше, гипотетическое приложение, должно выполнить очередь http запросов. При чем каждый следующий зависит от результата предыдущего.
async function main () {
let url = ' ... '
while (url) {
const resp = await fetch(url)
const json = await resp.json()
url = json.url
}
}
Но если посреди этого процесса пропадёт связь с интернетом, то очередь прервётся. Давайте исправим это.
Для начала напишем функцию, которая будет возвращать промис, который будет решатся, в момент, когда клиент возобновит соединение:
function awaitOnline () {
return new Promise(resolve => {
// Если клиент уже online — немедленно возвращаем результат
if (navigator.onLine) {
resolve()
return
}
// Регистрируем обработчик и решаем промис как только клиет будет online
window.addEventListener(
'online',
() => resolve(),
{once: true} // Автоматически удаляем обработчик после первого события
)
})
}
Теперь добавим её в наш основной код
async function main () {
let url = ' ... '
while (url) {
await awaitOnline() // Выполнение приостановится в этом месте до момента возобновления сети
const resp = await fetch(url)
const json = await resp.json()
url = json.url
}
}
Таким простым образом, при каждой итерации наш алгоритм будет проверять статус сети, и встанет на паузу если соединения не будет. И автоматически продолжит выполнение с того же места, как только связь будет восстановлена.
Конечно, это не панацея. Но такой, небольшой хак, сделает ваше приложение чуть более отказоустойчивым.
Комментарии (13)
IRT
17.01.2019 12:28В Chrome navigator.onLine показывает не фактическое нахождение в интернете, а лишь наличие соединения с LAN / роутером. Так что толку от navigator.onLine мало, попробуйте на этой странице нажать F12 и на вкладке Network поставить галку Offline, ничего не изменится:
bug336359.bmoattachments.org/attachment.cgi?id=220609
vintage
17.01.2019 13:44А повторять отвалившийся из-за оффлайна запрос кто будет?
Perlovich
17.01.2019 14:30имхо в большинстве случаев это лишнее и лучше просто показать пользователю сообщение, что имеются проблемы с сетью и попросить его перезагрузить страницу.
Так, во-первых, пользователь будет информирован, почему его приложение не отвечает на его действия (и перестанет тыкать элементы управления, вызывая ещё больше запросов). Во-вторых, так гораздо проще разрабатывать/поддерживать. Если мы хотим дать гарантию, что наше приложение нормально работает, если потерялось соединение, то это гораздо большее время тестирования и гораздо больше сценариев, которые надо проверить. Ну, и в третьих, если наш клиент автоматически пробует повторить запрос, то нужно дополнительно думать об идемпотентности PUT/POST/PATCH запросов.
Исключением являются ситуации, когда пользователь мог потратить большое количество времени, делая что-то (например, набирая текст статьи). Мы не хотим приводить пользователя в фрустрация, не сохранив всю его работу или хотя бы не предоставив ему возможность сохранить её вручную.vintage
17.01.2019 14:49Об идемпотентности надо думать даже, если пользователь вручную переотправляет запрос. Поэтому я POST запросы никогда не использую. Вообще, правильный апи, на мой взгляд реализует только два метода: GET и PUT/PATCH.
Zenitchik
17.01.2019 16:17имхо в большинстве случаев это лишнее и лучше просто показать пользователю сообщение, что имеются проблемы с сетью и попросить его перезагрузить страницу.
Ага. Пользователь проходил тест, на клиенте куча данных, открытая сессия SCORM, которую придётся переоткрывать. «Перезагрузить страницу»…
В тех случаях, когда нет необходимости повторять запрос — сообщать о проблемах с сетью необходимости тоже нет. Пользователь, чай, не дурак, 404 разглядит.
WanSpi
17.01.2019 14:14Заголовок слишком громкий, я думал тут покажут как остановить все скрипты одновременно…
coramba
17.01.2019 14:15Не совсем понятны два момента
1. зачем нужен while ($url)?
2. почему не сделать полностью на промисе?
awaitOnline() .then( // we're online ()=>fetch(url) .then(/*...*/) .catch(msg=>console.error(msg)); ) // something went wrong .catch(msg=>console.error(msg));
Kozack Автор
17.01.2019 14:201. Это бесконечный цикл, который будет выполнять один запрос за другим, до тех пор, пока в ответе приходит новый УРЛ. Это весьма утрированный пример, но он, как мне кажется, отлично демонстрирует общий алгоритм. В более реальном сценарии, это был бы какой-нибуть импорт большого числа коментариев на сервер (вам нужно получить ID созданного коментария, прежде чем импортировать дочерние), или скажем, обход всех страниц пагинации (вам нужно получить ссылку на следующую страницу, прежде чем загрузить записи).
2. Не совсем понял ваш вопрос. Мой пример тоже полностью основан на промисах.
propell-ant
Может всё-таки не каждый раз подписываться на событие window.online, а один раз подписаться и возвращать промис?
А концепция — да понятно.