Приостанавливаем выполнение приложения, если пропало соединение с сетью

    Под катом, небольшая заметка о том, как приостановить выполнение вашего приложения при обрыве связи с интернетом и продолжить — когда она будет восстановлена.

    Представим, что ваше, гипотетическое приложение, должно выполнить очередь 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
      }
    }
    

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

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

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 14

      0
      Может всё-таки не каждый раз подписываться на событие window.online, а один раз подписаться и возвращать промис?
      А концепция — да понятно.
        0
        В Chrome navigator.onLine показывает не фактическое нахождение в интернете, а лишь наличие соединения с LAN / роутером. Так что толку от navigator.onLine мало, попробуйте на этой странице нажать F12 и на вкладке Network поставить галку Offline, ничего не изменится:
        bug336359.bmoattachments.org/attachment.cgi?id=220609
          0
          Тут вы правы, но пологаю что можно заюзать события
          window.ononline и window.onoffline
          Хотя не факт что они поддерживаются всеми браузерами
          0
          А повторять отвалившийся из-за оффлайна запрос кто будет?
            0
            Так ведь смысл в том, что запрос не отправится, пока не появится сеть.
              0
              Я полагаю vintage имеет в виду случай, когда запрос уже отправлен, оборвалась сеть, и ответ не вернулся.
                0
                урл не перепишется и в следующий раз пойдет тот же запрос, но ексепшен словить да, было бы неплохо
              +1
              имхо в большинстве случаев это лишнее и лучше просто показать пользователю сообщение, что имеются проблемы с сетью и попросить его перезагрузить страницу.

              Так, во-первых, пользователь будет информирован, почему его приложение не отвечает на его действия (и перестанет тыкать элементы управления, вызывая ещё больше запросов). Во-вторых, так гораздо проще разрабатывать/поддерживать. Если мы хотим дать гарантию, что наше приложение нормально работает, если потерялось соединение, то это гораздо большее время тестирования и гораздо больше сценариев, которые надо проверить. Ну, и в третьих, если наш клиент автоматически пробует повторить запрос, то нужно дополнительно думать об идемпотентности PUT/POST/PATCH запросов.

              Исключением являются ситуации, когда пользователь мог потратить большое количество времени, делая что-то (например, набирая текст статьи). Мы не хотим приводить пользователя в фрустрация, не сохранив всю его работу или хотя бы не предоставив ему возможность сохранить её вручную.
                –1
                Об идемпотентности надо думать даже, если пользователь вручную переотправляет запрос. Поэтому я POST запросы никогда не использую. Вообще, правильный апи, на мой взгляд реализует только два метода: GET и PUT/PATCH.
                  0
                  имхо в большинстве случаев это лишнее и лучше просто показать пользователю сообщение, что имеются проблемы с сетью и попросить его перезагрузить страницу.

                  Ага. Пользователь проходил тест, на клиенте куча данных, открытая сессия SCORM, которую придётся переоткрывать. «Перезагрузить страницу»…

                  В тех случаях, когда нет необходимости повторять запрос — сообщать о проблемах с сетью необходимости тоже нет. Пользователь, чай, не дурак, 404 разглядит.
                0
                Заголовок слишком громкий, я думал тут покажут как остановить все скрипты одновременно…
                  0
                  Не совсем понятны два момента
                  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));
                  


                    0
                    1. Это бесконечный цикл, который будет выполнять один запрос за другим, до тех пор, пока в ответе приходит новый УРЛ. Это весьма утрированный пример, но он, как мне кажется, отлично демонстрирует общий алгоритм. В более реальном сценарии, это был бы какой-нибуть импорт большого числа коментариев на сервер (вам нужно получить ID созданного коментария, прежде чем импортировать дочерние), или скажем, обход всех страниц пагинации (вам нужно получить ссылку на следующую страницу, прежде чем загрузить записи).

                    2. Не совсем понял ваш вопрос. Мой пример тоже полностью основан на промисах.
                      0
                      Пардон, невнимательно прочел постановку задачи. Вопросы снимаю :)

                  Only users with full accounts can post comments. Log in, please.