Привет! В последнем релизе Chrome обнаружил очень необычное поведение браузера. Это поведение вызывало новые необычные ошибки в моём веб-скрипте. И я решил поделиться, как оптимизируют производительность Chrome, и про то, с какими необычными последствиями можно столкнуться.



Поехали.


Аномалия с которой мы столкнулись, выглядит так: 


  1. Пользователь пользуется веб-приложением.
  2. Потом пользователь нажимает кнопку в приложении. Эта кнопка сохраняет информацию на странице и открывает новую вкладку (с сохранённой информацией).
  3. В реальности в новой вкладке информация не сохранена. Это и есть наш баг. 
  4. Также присутствует аномалия: если после этого переключиться на первую вкладку (на секунду), и потом снова на новую вкладку, то информация становится сразу доступной на новой вкладке (после обновления страницы).

Причина аномалии


Это выглядит очень странно. Такое ощущение, что Хром полностью блокирует старую вкладку при переключении на новую. 
Оказалось, что недавно (релиз 57, 14 марта, 2017) Хромиум выпустил релиз с значительной оптимизацией производительности. Одна из оптимизаций?—?сокращение ресурсов, выделенных для работы фоновых вкладок. Пруф: https://blog.chromium.org/2017/03/reducing-power-consumption-for.html
Эта оптимизация и блокирует фоновую вкладку сразу (!) после открытия новой. Причём снижение производительности касается не только работы джаваскрипта, но и прочего API. Например, в нашем случае, начали фантастически медленно работать операции на IndexedDB в браузере. 


Как исправить


Чтобы обойти такие “особенности поведения”, нам пришлось вызывать собственные асинхронные коллбэки синхронно, и уменьшить зависимость от системных асинхронных API.

Поделиться с друзьями
-->

Комментарии (32)


  1. dartraiden
    21.03.2017 23:32
    +6

    1. Kaigorodov
      21.03.2017 23:45

      Да. Вот, кстати, оригинальный документ: Background tabs & offscreen frames: further plans


    1. Tufed
      22.03.2017 10:09

      Это теперь музыка онлайн-радио или любых музыкальных сервисов будет сразу затыкаться если я переключусь на другую вкладку? О_0


      1. bustEXZ
        22.03.2017 10:19
        +1

        Нет, об этому уже писали ранее. Если проигрываются звуки на странице, то вкладка блокироваться не будет


        1. Dimchansky
          22.03.2017 10:25
          +10

          Т.е. можно будет создать музыку с тишиной, чтобы вкладка не заблокировалась? :)


          1. bustEXZ
            22.03.2017 10:31
            -1

            помнится если включить музыку, но выключить звук, то иконка воспроизведения музыки пропадает на самом табе. По идее не сработает)


            1. Extremum
              22.03.2017 10:51
              +2

              Музыка с тишиной — это не выключенный звук.


              1. bustEXZ
                22.03.2017 11:20
                -1

                Да, я не так понял предложение. Ошибся. Хотя как можно сделать музыку с тишиной, тоже интересно)


          1. Aquahawk
            22.03.2017 20:27

            Ага, во флеше так делали. Например люди писали веб клиент к бирже, и клиенты любят открыть по 100 вкладок, чтобы столько сокетных соединений не висело и всё это не тормозило одна флешка была главной и содержала коннект к серверу, в другие по localConnection у неё что нужно забирали. Потом флеш начал стопориться на неактивной вкладке и всё встало. Так вот музыка поднимала fps обработчика event-loop до 8


      1. ZzoNe
        24.03.2017 21:34

        Конечно, нет, "Ограничение также не распространяется на фоновые вкладки, в которых используется API для воспроизведения звука, WebSockets или WebRTC". Это уже реализовано во некоторых браузерах, вот и chrome с оптимизацией подтянулся (пока что кривовато).


  1. NLO
    21.03.2017 23:46

    НЛО прилетело и опубликовало эту надпись здесь


    1. Hint
      22.03.2017 12:56
      +1

      Подскажите, если у меня сейчас аяксом раз в минуту через setTimeout отправляется запрос на сервер, получающий данные, то через что конкретно теперь это делать?


      1. jMas
        22.03.2017 13:19
        +2

        … через WebWorker? Держать данные в воркере, держать логику переодических обновлений там же. С целевой страницы (когда она живая) делать запросы к воркеру о получении актуальных данных. Судя по caniuse, актуальные браузеры поддерживают воркеры, но не могу говорить нюансах, потому что работал с ними только в Хроме.


        1. vitvad
          22.03.2017 17:26

          почему-то мне казалось что XMLHttpRequest не доступен в WebWorker, спасибо что развеяли мое заблуждение.


    1. Aingis
      22.03.2017 18:48
      +1

      По идее веб-воркеры тоже тормозятся. По крайней мере об этом рассказывали на 34-м MoscowJS. Впрочем, никто не обещает что и сервис-воркеры не затормозят, когда ими начнут сильно злоупотреблять. Так что лучше грамотно продумать архитектуру чем лепить такие костыли.


      1. NLO
        22.03.2017 20:25

        НЛО прилетело и опубликовало эту надпись здесь


    1. Kaigorodov
      22.03.2017 22:16

      К сожалению коллбэки, которые передаются в системные API в Workers вынести нельзя. Потому что само API их будет вызывать. И API в фоновых вкладках тоже затормаживается, то есть, например, IndexedDB тоже работает медленнее.
      Одних *Worker-ов недостаточно, чтобы решить проблему.


      1. NLO
        23.03.2017 02:37

        НЛО прилетело и опубликовало эту надпись здесь


        1. Kaigorodov
          23.03.2017 21:04

          IndexedDB есть в воркерах.

          API конечно есть, но оно же ведь в другом потоке работает.


  1. JackXB
    21.03.2017 23:47
    +1

    Если я не ошибаюсь, Safari тоже с фоновыми вкладками работает подобным образом.


    1. Kaigorodov
      21.03.2017 23:48

      Да, в Safari и вдруг браузерах есть похожие оптимизации.


      1. monah_tuk
        22.03.2017 09:56

        Гм, а я думал, почему всякие онлайн-банкинги так любят ссылки вида javascript:, как следствие полную невозможность открыть новую вкладку, оказывается и это workaround, дабы не случилось что-то, а пользователь потом не удивлялся (читать: возмущался/подавал в суд/ваш вариант).


        1. search
          22.03.2017 13:23
          +1

          В подавляющем большинстве случаев, ссылка вида javascript:... или #doAction говорит о том, что над страницей работала команда специалистов, неподозревающих о существовании тега <button>.


          1. monah_tuk
            22.03.2017 14:57

            Я подобных нюансов не знаю — не веб разработчик. В любом случае, не понятно, почему все (или почти все, проверить нужно) ссылки внутри (т.е. ссылки между разделами, а не внешние) банкингов AlfaClick и ВТБ24 имеют такой вид. Теперь, по крайней мере, есть одно объяснение, возможно связанное с Safari.


  1. koeshiro
    22.03.2017 10:42

    Главное что бы не пришлось перелопатить половину старых проектов =)


    1. Kaigorodov
      23.03.2017 20:51

      Боюсь, что придётся перелопачивать; но думаю, меньше 10ти процентов.


  1. fpinger
    22.03.2017 11:18

    А ещё бываю сайты, на которых при открытии новой вкладки в старой открывается реклама с другого сайта. И как приятно зная это старую вкладку закрыть без напряга для браузера.


  1. jMas
    22.03.2017 13:24

    Подскажите (вопрос к автору), а при уходе с вкладки выполняется unload? Возможно поможет Navigator.sendBeacon().


    1. Kaigorodov
      22.03.2017 22:08

      unload — нет. Но можно подписаться на visibilitychange. При этом я не уверен, будет ли он успевать выполняться.


      1. jMas
        23.03.2017 17:13

        Если будет дополнительная информация о тестировании этой фичи — поделитесь.


      1. jMas
        23.03.2017 17:30

        Сделал маленький тестик (index.html):


        <script>
        document.addEventListener("visibilitychange", function() {
          navigator.sendBeacon('http://0.0.0.0:8080/hello?s=' + document.visibilityState, '');
        });
        
        window.addEventListener("unload", function() {
          navigator.sendBeacon('http://0.0.0.0:8080/hello?s=unload', '');
        });
        </script>

        php -S 0.0.0.0:8080 -t ./

        И смотрите какие запросы падают в консоль при разных действиях.


        В хроме 56.0.2924 работает нормально, пока нет воможности потестировать в последнем, подскажите работает ли у вас?


        1. Kaigorodov
          23.03.2017 21:02
          +1

          Этот пример работает без проблем:


          PHP 5.6.28 Development Server started at Thu Mar 23 14:00:24 2017
          Listening on http://0.0.0.0:8080
          Document root is /Users/dkaigorodov/phproot
          Press Ctrl-C to quit.
          [Thu Mar 23 14:00:38 2017] 127.0.0.1:61159 [200]: /
          [Thu Mar 23 14:00:38 2017] 127.0.0.1:61161 [404]: /favicon.ico - No such file or directory
          [Thu Mar 23 14:00:47 2017] 127.0.0.1:61182 [200]: /hello?s=hidden
          [Thu Mar 23 14:00:49 2017] 127.0.0.1:61196 [200]: /hello?s=visible
          [Thu Mar 23 14:00:50 2017] 127.0.0.1:61201 [200]: /hello?s=hidden
          [Thu Mar 23 14:00:58 2017] 127.0.0.1:61234 [200]: /hello?s=visible
          [Thu Mar 23 14:00:58 2017] 127.0.0.1:61235 [200]: /hello?s=hidden
          [Thu Mar 23 14:00:59 2017] 127.0.0.1:61240 [200]: /hello?s=visible
          [Thu Mar 23 14:01:02 2017] 127.0.0.1:61253 [200]: /hello?s=hidden
          [Thu Mar 23 14:01:02 2017] 127.0.0.1:61254 [200]: /hello?s=unload