Почему в последнее время тормозит Pikabu?

А потому что кто-то криворукий выполняет AJAX запросы в синхронном режиме.

Почему в последнее время тормозит Pikabu? Баг на Пикабу, Багрепорты, Маразм

Breakpoint на все AJAX-запросы, буквально второй же запрос.

В функции xhr.open передаётся до 5 параметров: метод ("GET"), URL запроса, флаг асинхронности, имя пользователя и пароль (для basic-авторизации).

Когда флаг асинхронности установлен в false, то браузер подвисает до тех пор, пока запрос не выполнится. Логично, что никто такие запросы теперь уже не использует. Единственный полезный вариант их использования - в событии beforeunload, чтобы можно было отправить какие-то данные на сервер до того, как вкладка закроется.

@SupportTech, какого хрена? Вы там код ревью вообще проводите? Какой вообще смысл делать синхронные запросы?

P. S. Про eval'ы через eval'ы я вообще молчу...

Багрепорты

7.1K поста1.1K подписчиков

Добавить пост

Правила сообщества

Опишите подробно:

- Возникшую проблему

- Порядок действий для повторения проблемы

- Версию Пикабу: ПК, мобильная браузерная, приложение Android, приложение iOS

- Ваше устройство, ОС, браузер


Приложите скриншоты или видео с проблемой

И мы постараемся помочь :)

92
Автор поста оценил этот комментарий
ответный пост

UPD от администрации: убедились с пользователем, что тормоза не связаны с Адблоком: #comment_284244345. Автор удалил свою статью с Хабра.

Итак, копнём чуть поглубже и убедимся, что @pikabu и @SupportTech поступают, как мудаки. Заодно делаем ставки, за сколько этот пост удалят, а меня заблокируют.

Преабула: в последнее время у многих людей тормозит сайт. Здесь мы разберёмся, почему это происходит. Внимание, много минифициваронного кода!

Для ЛЛ: если в браузере запущен AdBlock, то в код страницы встраивается скрипт, специально тормозящий сайт.

При загрузке сайта в app.js выполняется вот такая милая функция:

Продолжение поста «Почему в последнее время тормозит Pikabu?» Пикабу, Нововведение, Web-программирование, Adblock, Жалоба, Ответ на пост, Длиннопост

Что она делает? Она создаёт невидимый элемент с именами классов, соответствующих рекламе. В этом можно убедиться, поставив точку останова непосредственно при добавлении этого элемента:

Продолжение поста «Почему в последнее время тормозит Pikabu?» Пикабу, Нововведение, Web-программирование, Adblock, Жалоба, Ответ на пост, Длиннопост

Далее происходит небольшая пауза (o.W вызывает requestAnimationFrame), после которой в цикле с небольшими задержками (i.g ведёт на setTimeout) проверяется, не изменился ли стиль этого элемента определённым образом (см. функцию h). Если изменился - значит AdBlock присутствует и скрыл этот элемент, как рекламу. Если изменений нет - то и AdBlock нет, всё просто.

Результат этой функции передаётся сюда:

Продолжение поста «Почему в последнее время тормозит Pikabu?» Пикабу, Нововведение, Web-программирование, Adblock, Жалоба, Ответ на пост, Длиннопост

K.P - это та самая функция определения AdBlock, что выше.

Проверяется домен (видимо, чтобы на localhost у разработчиков не срабатывало). Далее в cookie с названием "bs" пишется определённое значение: латинская буква и цифра ("K0", "A1"). Если AdBlock присутствует, пишется "1". Если отсутствует - пишется "0".

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

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

Продолжение поста «Почему в последнее время тормозит Pikabu?» Пикабу, Нововведение, Web-программирование, Adblock, Жалоба, Ответ на пост, Длиннопост

Весь вот этот блок целиком, да. Особенно интересна вот эта часть:

Продолжение поста «Почему в последнее время тормозит Pikabu?» Пикабу, Нововведение, Web-программирование, Adblock, Жалоба, Ответ на пост, Длиннопост

Во-первых, этот код делает синхронный XHR-запрос к серверу. Синхронные запросы отличаются тем, что заставляют вкладку браузера подвиснуть до тех пор, пока запрос не будет выполнен. Этим и обусловлены тормоза у пользователей - они происходят из-за того, что браузер выполняет синхронный запрос к серверу. Для того, чтобы сделать запрос асинхронным и ничего не тормозить, достаточно добавить true третьим параметром. Но тогда пользователи ведь продолжат пользоваться AdBlock, верно?

Во-вторых, этот код скачивает непонятно какой скрипт с сервера и выполняет его. В скрипте может содержать что угодно. Но в принципе, это мелочи, мы и так не можем контролировать скрипты на сайте.

При этом товарищи из @SupportTech предлагают отключать расширения, в том числе AdBlock, чтобы ничего не тормозило. Логично, вы ведь сами добавили тормоза, если AdBlock включен.

@Pikabu, @SupportTech, ваши комментарии?

P. S. Запрещаю переносить этот пост в сообщества, так как это не является багрепортом или предложением по сайту.

Показать полностью 4
комментарии (44)
1
Автор поста оценил этот комментарий

Ничего не подвисает, в браузере(во вкладке) множество потоков выполнения, один из потоков выполнения дожидается результата запроса чтобы выполнить логику которой нужен результат запроса. Автор поста плохо знаком с архитектурой фронтенд приложений.

раскрыть ветку (1)
2
Автор поста оценил этот комментарий

Ну зашибись, плохо знаком... Для разнообразия даже отвечу развёрнуто.

Современные веб-браузеры - штука многопроцессная и многопоточная. Один процесс отвечает за одну или несколько вкладок плюс веб воркеры. Внутри каждого процесса есть несколько потоков со своими задачами: работа с сетью, парсинг DOM, рендер страницы, выполнение скриптов и т. п.

На каждую страницу выделяется одна среда для выполнения скриптов. Современный браузерный Javascript - асинхронный однопоточный. Это значит, что если запустить какую-то одну большую задачу (типа бесконечного цикла), то скриптовый движок будет её обрабатывать до упора, и передаст управление дальше только по завершении. Различные события, коллбэки и промисы дают возможность асинхронный работы: определённые куски кода вызываются по событию из внутренней очереди. Но в один момент времени для определённой среды может выполняться только один скрипт. Точно так же устроена асинхронная модель в тех же Python или nginx. См. устройство libevent или libev для более детального примера.

Настоящей многопоточности для яваскрипта в браузерах нет. Для этих целей рекомендуется использовать веб воркеры либо WebAssembly (которые выполняются в отдельных, независимых потоках). Зато разработчикам не приходится возиться с синхронизацией потоков.

В браузере, помимо скриптового движка, есть ещё куча механизмов. Они тоже обмениваются событиями и периодически могут блокировать друг друга.

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

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

Многопоточность, блин, не означает, что никогда ничего не подвисает. Это зависит от архитектуры приложения. Разберитесь и изучите сначала сами про многопоточку, асинхронность и межпроцессное взаимодействия, прежде чем кидаться словами, что кто-то плохо знаком с чем-то.

И да, если уж мне не верите, см. страничку от Mozilla https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequ... :

> Synchronous requests block the execution of code which causes "freezing" on the screen and an unresponsive user experience.

показать ответы
Автор поста оценил этот комментарий

Другие кнопки продолжат работать.

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

https://codepen.io/amitkulk123/pen/NGxEMx

Да лаааааадно? То есть страница не подвиснет и её можно будет скроллить и взаимодействовать с ней?

Вообще движок может определить бесконечный цикл и прервать его через какое-то время. А может и не прервать.

Автор поста оценил этот комментарий

Я прекрасно понимаю в отличии от вас.

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Тогда объясните подробно, в чём я, идиот, заблуждаюсь. С объяснениями, как оно якобы "на самом деле" работает, и почему синхронные AJAX-запросы не могут приводить к фризам в браузере - хотя про это написано и у Mozilla, и у Microsoft, и у всех подряд:

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequ...

https://learn.microsoft.com/en-us/power-apps/developer/model...

показать ответы
Автор поста оценил этот комментарий

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

раскрыть ветку (1)
1
Автор поста оценил этот комментарий

Хватит уже нести ахинею, идите и учитесь нормально, а не по всяким курсам (или где вы там учились). Да, теперь уже я кидаюсь словами, но меня уже бесит подобная упёртая безграмотность.

Переключением между потоками руководит планировщик в ядре ОС.

Потоки могут блокироваться. Один поток внутри процесса может заблокировать выполнение других. Гуглите про мьютексы, семафоры, атомарные операции и состояние гонки. Последнее - это когда два потока взаимно блокируют друг друга и подвисают нафиг.

В случае синхронных XHR-запросов поток с движком JS блокируется до тех пор, пока не будет получен ответ. Эта блокировка может привести к блокировке других потоков.

показать ответы
Автор поста оценил этот комментарий

Приветствую.
Уточните, пожалуйста, при авторизации в режиме инкогнито без каких-либо активных расширений наблюдается ли для вас аналогичное поведение?

раскрыть ветку (1)
Автор поста оценил этот комментарий

Я колбек в синхронном коде могу вызвать, в чем трудности то?

раскрыть ветку (1)
Автор поста оценил этот комментарий

Так если его вызвать синхронно, код от этого асинхронным не станет.

Автор поста оценил этот комментарий

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

раскрыть ветку (1)
Автор поста оценил этот комментарий

...ладно, пойдём другим путём.

Допустим, на странице есть какая-то анимация и кнопка. В яваскриптовом коде есть обработчик её нажатия, который запускает бесконечный цикл. Если нажать на кнопку, эта вкладка подвиснет или нет?

показать ответы
Автор поста оценил этот комментарий

запускается еще 1 поток выполнения, а как же еще.

раскрыть ветку (1)
Автор поста оценил этот комментарий

Используя non-blocking IO, как вариант, не? В таком случае события IO обрабатываются очередью в одном потоке.

https://www.jmoisio.eu/en/blog/2020/10/13/non-blocking-serve...

Автор поста оценил этот комментарий

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

раскрыть ветку (1)
Автор поста оценил этот комментарий

Отлично. "Дождёмся выполнения запроса". Как именно происходит это ожидание, что при этом происходит с потоком, в котором выполняется яваскриптовый движок?

Автор поста оценил этот комментарий

Причем тут вообще колбеки?

раскрыть ветку (1)
Автор поста оценил этот комментарий

*рукалицо*

А как, по вашему, работает асинхронный код?

показать ответы
Автор поста оценил этот комментарий

По поводу учебы, зачем мне учится? У меня проблем с работой нет.

раскрыть ветку (1)
Автор поста оценил этот комментарий

Чтобы понимать, как работают процессы и потоки в системе, и перестать нести подобный бред.

показать ответы
Автор поста оценил этот комментарий

Как бы да, но есть нюанс, у нас здесь средой выполнения является браузер и в данном случае ядро браузера руководит тем, что выполняется в браузере. А ядром браузера уже руководит ОС. Также и с java, python, Node.js. Представьте себе ОС запущенную в виртуальной машине, браузер по сути тоже самое.

раскрыть ветку (1)
Автор поста оценил этот комментарий

Я этот бред даже комментировать не буду. Вы уже умудрились перепутать процессы, потоки, легковесные потоки (green threads) и кто чем руководит.

https://chromium.googlesource.com/chromium/blink.git/+/99b8c... - изучайте, начиная отсюда.

https://chromium.googlesource.com/chromium/blink.git/+/99b8c... - продолжайте изучение тут.

Обратите внимание, что при m_async == true будут вызваны коллбэки из https://chromium.googlesource.com/chromium/blink.git/+/99b8c...

показать ответы