Нарушение Долгосрочная задача JavaScript заняла хх мс

331

Недавно я получил такое предупреждение, и это мой первый раз:

[Violation] Long running JavaScript task took 234ms
[Violation] Forced reflow while executing JavaScript took 45ms

Я работаю над групповым проектом, и я понятия не имею, откуда это исходит. Этого никогда не было раньше. Внезапно это появилось, когда кто-то еще вмешался в проект. Как мне найти, какой файл / функция вызывает это предупреждение? Я искал ответ, но в основном о том, как его решить. Я не могу решить это, если я даже не могу найти источник проблемы.

В этом случае предупреждение появляется только в Chrome. Я пытался использовать Edge, но я не получил подобных предупреждений, и я еще не тестировал его в Firefox.

Я даже получаю ошибку от jquery.min.js:

[Violation] Handler took 231ms of runtime (50ms allowed)            jquery.min.js:2
procatmer
источник
Где вы видите это предупреждение? Вы не говорите, в какой среде вы работаете. Предполагая какой-то браузер, но какой и т.д.?
Сами Кухмонен
3
@SamiKuhmonen извините за это, я обновил свой вопрос. Я использовал Chrome. Я не нашел подобной ошибки на Edge.
комментатор
8
Я просто хотел добавить, что это предупреждение, появившееся в конце 2016 года, может также появиться из-за любых расширений, которые вы, возможно, установили в Chrome. Это легко проверить, протестировав в приватном режиме.
Fer
1
Нажав на ссылку справа, указывающую на сценарий, где происходит нарушение, вы попадете в то место кода, где оно происходит.
bluehipy
Я использую Ionic 4 (Angular 8), мой код работал нормально, внезапно начали появляться подобные нарушения - в моем списке сейчас нет данных?
Капил Рагхуванши,

Ответы:

278

Обновление : Chrome 58+ скрыл эти и другие сообщения отладки по умолчанию. Чтобы отобразить их, нажмите стрелку рядом с «Информация» и выберите «Подробно».

Chrome 57 по умолчанию включил «скрыть нарушения». Чтобы включить их снова, необходимо включить фильтры и снять флажок «скрывать нарушения».

внезапно появляется, когда кто-то еще участвует в проекте

Я думаю, что вы, скорее всего, обновили Chrome 56. Это предупреждение - замечательная новая функция, на мой взгляд, пожалуйста, отключайте ее, только если вы в отчаянии и ваш оценщик уберет у вас оценки. Основные проблемы есть в других браузерах, но браузеры просто не сообщают вам, что есть проблема. Билет Chromium здесь, но на самом деле нет никакой интересной дискуссии по нему.

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

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

function someMethodIThinkMightBeSlow() {
    const startTime = performance.now();

    // Do the normal stuff for this function

    const duration = performance.now() - startTime;
    console.log(`someMethodIThinkMightBeSlow took ${duration}ms`);
}

Если вы хотите стать более продвинутым, вы также можете использовать профилировщик Chrome или использовать такую ​​библиотеку для сравнения, как эта .

Как только вы нашли некоторый код, который занимает много времени (50 мс - порог Chrome), у вас есть несколько вариантов:

  1. Вырежьте часть / всю эту задачу, которая может быть ненужной
  2. Узнайте, как выполнить ту же задачу быстрее
  3. Разделите код на несколько асинхронных шагов

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

setTimeout(functionToRunVerySoonButNotNow);

или

// This one is not available natively in IE, but there are polyfills available.
Promise.resolve().then(functionToRunVerySoonButNotNow);

Вы можете прочитать больше об асинхронной природе JavaScript здесь .

voltrevo
источник
16
Просто предложение, вместо того, чтобы использовать performance.now(), вы можете использовать console.time( developer.mozilla.org/en-US/docs/Web/API/Console/time ) console.time('UniquetLabelName') ....code here.... console.timeEnd('UniqueLabelName')
denislexic
@ denislexic Я так думаю. Я не уверен, какое значение это действительно добавляет, хотя. Я бы сказал, что изучение основной операции - получение текущего времени и его использование - более ценно.
Вольтрево
34
Отличный ответ, вольтрево! Мой вопрос: если такой код является нарушением, что именно является нарушением? Должен быть какой-то стандарт, который применяет Google, но публично ли он документирован?
Bungler
1
@Bungler Не знаю, я хотел бы знать, есть ли какое-то руководство, на которое оно также ссылается.
Вольтрево
4
@Bungler Я могу только догадываться, что это говорит о том, что код, который анимируется, нарушает обеспечение как минимум 60 кадров в секунду и, следовательно, плохой пользовательский опыт. ,
user895400
90

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

Что заставляет верстку / оплавление

Все нижеприведенные свойства или методы, при запросе / вызове в JavaScript, будут запускать браузер для синхронного вычисления стиля и макета *. Это также называется перекомпоновкой или компоновкой макета , и является обычным узким местом производительности.

Элемент

Box метрики
  • elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight,elem.offsetParent
  • elem.clientLeft, elem.clientTop, elem.clientWidth,elem.clientHeight
  • elem.getClientRects(), elem.getBoundingClientRect()
Прокрутка вещи
  • elem.scrollBy(), elem.scrollTo()
  • elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()
  • elem.scrollWidth, elem.scrollHeight
  • elem.scrollLeft, elem.scrollTopТакже, устанавливая их
фокус
  • elem.focus() может вызвать двойной принудительный макет ( источник )
Также…

getComputedStyle

window.getComputedStyle()будет обычно вызывать стиль recalc ( источник )

window.getComputedStyle() также принудительно выполнит компоновку, если выполнено любое из следующих действий:

  1. Элемент находится в теневом дереве
  2. Есть медиа-запросы (связанные с окном просмотра). В частности, одно из следующих действий : ( источник ) * min-width, min-height, max-width, max-height, width, height * aspect-ratio, min-aspect-ratio,max-aspect-ratio
    • device-pixel-ratio, resolution,orientation
  3. Запрашиваемое свойство является одним из следующих: ( источник )
    • height, width * top, right, bottom, left * margin[ -top, -right, -bottom, -left, Или сокращенная ] только , если граница фиксируется. * padding[ -top, -right, -bottom, -left, Или сокращенная ] только если обивка фиксируется. * transform, transform-origin, perspective-origin * translate, rotate, scale * webkit-filter, backdrop-filter * motion-path, motion-offset, motion-rotation * x, y, rx,ry

окно

  • window.scrollX, window.scrollY
  • window.innerHeight, window.innerWidth
  • window.getMatchedCSSRules() только силы стиля

формы

  • inputElem.focus()
  • inputElem.select(), textareaElem.select()( источник )

События мыши

  • mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY ( Источник )

документ

  • doc.scrollingElement только силы стиля

Ассортимент

  • range.getClientRects(), range.getBoundingClientRect()

SVG

contenteditable

  • Много-много всего, включая копирование изображения в буфер обмена ( источник )

Проверьте больше здесь .

Кроме того, вот исходный код Chromium из оригинального выпуска и обсуждение API производительности для предупреждений.


Изменить: Есть также статья о том, как минимизировать перекомпоновку макета на PageSpeed ​​Insight от Google . Это объясняет, что такое перекомпоновка браузера:

Reflow - это имя процесса веб-браузера для перерасчета позиций и геометрии элементов в документе с целью повторного рендеринга части или всего документа. Поскольку reflow является операцией блокировки пользователя в браузере, разработчикам полезно понять, как улучшить время перекомпоновки, а также понять влияние различных свойств документа (глубина DOM, эффективность правил CSS, различные типы изменений стиля) на перекомпоновку. время. Иногда для перекомпоновки одного элемента в документе может потребоваться перекомпоновка его родительских элементов, а также любых элементов, следующих за ним.

Кроме того, это объясняет, как минимизировать это:

  1. Уменьшите ненужную глубину DOM. Изменения на одном уровне в дереве DOM могут вызвать изменения на каждом уровне дерева - вплоть до корня и вплоть до потомков модифицированного узла. Это приводит к увеличению времени, затрачиваемого на выполнение оплавления.
  2. Минимизируйте правила CSS и удалите неиспользуемые правила CSS.
  3. Если вы делаете сложные изменения рендеринга, такие как анимация, делайте это из потока. Используйте абсолютное положение или фиксированное положение для достижения этой цели.
  4. Избегайте ненужных сложных CSS-селекторов - в частности, потомков-селекторов - которые требуют больше ресурсов процессора для сопоставления селекторов.
новичек
источник
1
Дополнительные сведения: исходный код Chromium из оригинальной версии и обсуждение API производительности для предупреждений.
Робокат
1
В соответствии с вышеизложенным, простое чтение element.scrollTop запускает перекомпоновку. Это кажется мне нелогичным явлением. Я могу понять, почему установка element.scrollTop вызовет перекомпоновку, а просто прочитает ее значение? Может кто-то объяснить, почему это так, если это действительно так?
Дэвид Эдвардс
29

Пара идей:

  • Удалите половину вашего кода (возможно, закомментировав его).

    • Проблема все еще там? Отлично, вы сузили возможности! Повторение.

    • Проблема не в этом? Хорошо, посмотрите на половину, которую вы закомментировали!

  • Используете ли вы какую-либо систему контроля версий (например, Git)? Если так, git checkoutто некоторые из ваших последних коммитов. Когда возникла проблема? Посмотрите на коммит, чтобы увидеть, какой именно код изменился, когда впервые появилась проблема.

therobinkim
источник
Спасибо за ваш ответ. я удалил половину и даже исключил мой основной файл .js из проекта. как-то ошибка все же произошла. Вот почему я так расстраиваюсь из-за этого. и да, я использую Git. Я только что понял эту ошибку сегодня. с тех пор, как это стало групповым проектом, было много коммитов. может сделать глубокую проверку. Еще раз спасибо за идеи.
комментатор
@procatmer использует ту же стратегию для поиска git commit. Например, если бы у меня было 10 коммитов (A, B, C, D, E, F, G, H, I, J), где A был самым старым, я бы git checkout Eпосмотрел, существует ли проблема. Если да, я продолжу искать проблему в первой половине коммитов. В противном случае я ищу проблему во втором тайме.
therobinkim
1
@procatmer Кроме того, если вы не указали свой основной .jsфайл и проблема не исчезла ... это может быть библиотека, которую вы добавили с помощью <script src="...">тега! Может быть, о чем-то не стоит беспокоиться (тем более что это всего лишь предупреждение)?
therobinkim
1
я наконец нашел, где проблема. Я использовал твою вторую идею, чтобы отслеживать изменения. и да, проблема исходит из внешнего .jsфайла. по-видимому, это имеет значение. это значительно замедляет мой сайт. В любом случае, еще раз спасибо за ваши ответы и идеи.
комментатор
2
Вы можете использовать git bisect, чтобы применить бинарный поиск. Я думаю, что это просто с целью поиска ошибок.
Пиетровисмара
12

Чтобы определить источник проблемы, запустите приложение и запишите его на вкладке «Производительность» Chrome .

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

Проверьте эти файлы и попытайтесь определить, является ли это кодом какого-либо расширения или вашим. (Если это ваше, значит, вы нашли источник вашей проблемы.)

Мэтт Леонович
источник
Нет, у меня нет AdBlock, и я все еще получаю его в консоли.
Никола Стоякович
Попробуйте проанализировать его с помощью вкладки «Производительность» и найдите источник функций, которые выполняются долгое время. Это может быть что угодно, но это потенциальный способ определить источник проблемы.
Мэтт Леонович
6

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

В моем случае был набор сценариев добавления Angular, которые я включил, но еще не использовал в приложении:

<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.8/angular-ui-router.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-utils/0.1.1/angular-ui-utils.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-animate.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-aria.min.js"></script>

Это были единственные файлы JavaScript, которые загружались дольше, чем время, указанное в сообщении об ошибке «Long Running Task».

Все эти файлы запускаются на других моих веб-сайтах без сгенерированных ошибок, но я получаю эту ошибку "Long Running Task" в новом веб-приложении, которое едва имело какую-либо функциональность. Ошибка прекратилась сразу после удаления.

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

Джордан Реддик
источник
6

Я нашел корень этого сообщения в своем коде, который искал и скрывал или показывал узлы (в автономном режиме). Это был мой код:

search.addEventListener('keyup', function() {
    for (const node of nodes)
        if (node.innerText.toLowerCase().includes(this.value.toLowerCase()))
            node.classList.remove('hidden');
        else
            node.classList.add('hidden');
});

Вкладка производительности (профилировщик) показывает, что событие занимает около 60 мс: Chromium Performance Profiler макет перерасчета перекомпоновки

Сейчас:

search.addEventListener('keyup', function() {
    const nodesToHide = [];
    const nodesToShow = [];
    for (const node of nodes)
        if (node.innerText.toLowerCase().includes(this.value.toLowerCase()))
            nodesToShow.push(node);
        else
            nodesToHide.push(node);

    nodesToHide.forEach(node => node.classList.add('hidden'));
    nodesToShow.forEach(node => node.classList.remove('hidden'));
});

Вкладка производительности (профилировщик) теперь показывает, что событие занимает около 1 мс: Хромовый профилировщик темный

И я чувствую, что поиск теперь работает быстрее (229 узлов).

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

Я нашел решение в исходном коде Apache Cordova. Они реализуют так:

var resolvedPromise = typeof Promise == 'undefined' ? null : Promise.resolve();
var nextTick = resolvedPromise ? function(fn) { resolvedPromise.then(fn); } : function(fn) { setTimeout(fn); };

Простая реализация, но умный способ.

Над Android 4.4 используйте Promise. Для старых браузеров используйтеsetTimeout()


Использование:

nextTick(function() {
  // your code
});

После вставки этого кода, все предупреждающие сообщения исчезли.

wf9a5m75
источник
2

Если вы используете Chrome Canary (или бета-версию), просто установите флажок «Скрыть нарушения».

Флажок «Скрыть нарушения» в консоли Chrome 56

Чжаомин
источник
1

Это ошибка нарушения от Google Chrome, которая показывает, когда Verboseуровень ведения журнала включен.

Пример сообщения об ошибке:

скриншот предупреждения

Объяснение:

Reflow - это имя процесса веб-браузера для перерасчета позиций и геометрии элементов в документе с целью повторного рендеринга части или всего документа. Поскольку reflow является операцией блокировки пользователя в браузере, разработчикам полезно понять, как улучшить время перекомпоновки, а также понять влияние различных свойств документа (глубина DOM, эффективность правил CSS, различные типы изменений стиля) на перекомпоновку. время. Иногда для перекомпоновки одного элемента в документе может потребоваться перекомпоновка его родительских элементов, а также любых элементов, следующих за ним.

Оригинальная статья: Минимизация перекомпоновки браузера Линдси Саймон, разработчиком UX, опубликована на developers.google.com.

И эта ссылка Google Chrome дает вам в профилировщике производительности, в профилях макетов (лиловые регионы), для получения дополнительной информации о предупреждении.

Пол-Себастьян Маноле
источник
0

Добавление моих идей здесь, поскольку эта тема была вопросом «перейти к» stackoverflow по теме.

Моя проблема была в приложении Material-UI (ранние стадии)

  • причиной был выбор поставщика Темы

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

Все было хорошо, пока я не обновил «состояние», которое заставляет «компонент результатов» перерисовываться. Основная проблема заключалась в том, что у меня была тема материала-интерфейса ( https://material-ui.com/customization/theming/#a-note-on-performance ) в том же устройстве визуализации (App.js / return ..) в качестве «компонента результатов», SummaryAppBarPure

Решение состояло в том, чтобы поднять ThemeProvider на один уровень вверх (Index.js) и обернуть здесь компонент App, не заставляя ThemeProvider пересчитывать и рисовать / layout / reflow.

перед

в App.js:

  return (
    <>
      <MyThemeProvider>
      <Container className={classes.appMaxWidth}>

        <SummaryAppBarPure
//...

в index.js

ReactDOM.render(
  <React.StrictMode>
      <App />
//...

после

в App.js:

return (
    <>
      {/* move theme to index. made reflow problem go away */}
      {/* <MyThemeProvider> */}
      <Container className={classes.appMaxWidth}>

        <SummaryAppBarPure
//...

в index.js

ReactDOM.render(
  <React.StrictMode>
    <MyThemeProvider>
      <App />
//...
JimiSweden
источник
-2

Принудительное перекомпонование часто происходит, когда у вас есть функция, вызванная несколько раз до конца выполнения.

Например, у вас может быть проблема на смартфоне, но не на классическом браузере.

Я предлагаю использовать setTimeoutдля решения проблемы.

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

  1. Выключите вызовы «1 на 1» и перезагрузите код, чтобы увидеть, все равно ли он выдает ошибку.
  2. Если второй скрипт вызывает ошибку, используйте setTimeOutоснованный на продолжительности нарушения.
Cherif
источник
Это не решение. Это предложение лучше оставить в качестве комментария к исходному вопросу.
Пол-Себастьян Маноле
-6

Это не ошибка, просто сообщение. Чтобы выполнить это сообщение, измените
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">(пример)
на
<!DOCTYPE html>(источник Firefox ожидает этого)

Сообщение было показано в Google Chrome 74 и Opera 60. После смены было понятно, 0 многословно.
Подход к решению

Gerdixx
источник
5
Просто несколько советов: ваш ответ не имеет ничего общего с вопросами. Либо исправьте свой ответ, либо удалите его. Вопрос был «почему консоль браузера Chrome показывает предупреждение о нарушении». Ответ заключается в том, что это функция в новых браузерах Chrome, где она предупреждает вас, если веб-страница вызывает чрезмерную перекомпоновку браузера при выполнении JS. Пожалуйста, обратитесь к этому ресурсу от Google для получения дополнительной информации.
Пол-Себастьян Маноле