Порушення Тривале завдання JavaScript зайняло xx мс


330

Нещодавно я отримав таке попередження, і це вперше я його отримую:

[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

Де ви бачите це попередження? Ви не кажете, в якому середовищі працюєте. Припустимо, якийсь браузер, але який і т.д.?
Самі Кухмонен

3
@SamiKuhmonen вибачте за це, я оновив своє запитання. я використовував Chrome. я не знайшов подібних помилок на Edge.
прокамер

8
Я просто хотів додати, що це попередження, представлене наприкінці 2016 року, також може з’являтися через будь-які розширення, які ви, можливо, встановили в Chrome. Це легко перевірити, тестуючи в приватному режимі.
Фер

1
Клацнувши на правому боковому посиланні, вказуючи вам сценарій, де відбуваються порушення, ви перейдете на місце в коді, де це відбувається.
bluehipy

Я використовую Ionic 4 (Angular 8), мій код працював нормально, раптом таке порушення почало надходити - зараз у моєму списку немає даних, що відображаються?
Kapil Raghuwanshi

Відповіді:


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 ви можете прочитати тут .


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 Я думаю, що так. Я не впевнений, яке значення дійсно додає, хоча. Я б заперечував, що дізнатися про основні операції, як отримати поточний час та базуватись на цьому, є більш цінним.
voltrevo

34
Чудова відповідь, вольтрево! Моє запитання: якщо такий код є порушенням, то що саме це порушує? Має бути якийсь стандарт, який застосовує Google, але чи є цей стандарт публічно задокументований де-небудь?
Банглер

1
@ Buungler Dunno, я хотів би знати, чи є також якісь настанови, на які це стосується.
voltrevo

4
@ Buungler Я можу лише здогадуватися, що це говорить про те, що анімаційний код порушує надання щонайменше 60 кадрів в секунду і тому дає поганий досвід користувачеві. .
користувач895400

90

Це лише попередження, як згадували всі. Однак якщо ви прагнете вирішити ці (що вам слід), то вам слід спочатку визначити, що викликає попередження. Немає жодної причини, з-за якої можна отримати попередження про поглиблення сили. Хтось створив список для деяких можливих варіантів. Ви можете слідкувати за обговоренням для отримання додаткової інформації.
Ось суть можливих причин:

Що змушує компонувати / відновлювати

Усі наведені нижче властивості або методи, коли запитуються або викликаються в JavaScript, запускають браузер синхронно обчислювати стиль та макет *. Це також називається поповненням або обваленням макета , і це звичайне вузьке місце.

Стихія

Поле метрики
  • 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() може викликати подвійну вимушену компонування ( джерело )
Також…
  • elem.computedRole, elem.computedName
  • elem.innerText( джерело )

getComputedStyle

window.getComputedStyle()як правило, примушуватиме виклик стилю ( джерело )

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

зміст

  • Багато та багато речей, включаючи копіювання зображення у буфер обміну ( джерело )

Перевірте більше тут .

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


Редагувати . Також є стаття про те, як мінімізувати поповнення макета в Google PageSpeed ​​Insight від Google . Це пояснює, що таке поповнення браузера:

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

Крім того, він пояснює, як мінімізувати це:

  1. Зменшити зайву глибину DOM. Зміни на одному рівні дерева DOM можуть спричинити зміни на кожному рівні дерева - аж до кореня і аж до дітей модифікованого вузла. Це призводить до того, що більше часу витрачається на виконання заправ.
  2. Мінімізуйте правила CSS та видаліть невикористані правила CSS.
  3. Якщо ви робите складні зміни візуалізації, такі як анімація, виконайте це поза потоком. Використовуйте абсолютну позицію або фіксовану позицію для цього.
  4. Уникайте зайвих складних селекторів CSS - зокрема селекторів, які потребують більшої потужності процесора для відповідності селектора.

1
Додаткові відомості: вихідний код Chromium з оригіналу випуску та обговорення API ефективності для попереджень.
robocat

1
Згідно з вищезазначеним, просто читання element.scrollTop запускає поповнення. Це вражає мене явищем контр-інтуїтивного. Я можу зрозуміти, чому встановлення element.scrollTop викликає поповнення, а просто зчитування його значення? Чи може хтось далі пояснити, чому це так, якщо це дійсно так?
Девід Едвардс

29

Пара ідей:

  • Видаліть половину свого коду (можливо, коментуючи його).

    • Чи проблема все ж є? Чудово, ви звузили можливості! Повторіть.

    • Хіба проблеми немає? Гаразд, подивіться на половину, яку ви прокоментували!

  • Ви використовуєте будь-яку систему контролю версій (наприклад, Git)? Якщо так, то git checkoutдеякі ваші новіші зобов’язання. Коли була введена проблема? Подивіться на комісію, щоб точно побачити, який код змінився, коли проблема вперше надійшла.


Спасибі за вашу відповідь. Я видалив половину і навіть виключив головний файл .js з проекту. якось помилка все-таки сталася. саме тому я так засмучуюсь. і так, я використовую git. Я щойно зрозумів цю помилку сьогодні. З того часу, як це стало груповим проектом, було зроблено багато комісій. може зробити глибоку перевірку. ще раз дякую за ідеї.
прокамер

@procatmer використовує ту саму стратегію, щоб знайти git. Наприклад, якби у мене було 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, щоб застосувати двійковий пошук. Я думаю, що це просто з метою пошуку помилок.
pietrovismara

12

Щоб визначити джерело проблеми, запустіть програму та запишіть її на вкладці Ефективність Chrome .

Там ви можете перевірити різні функції, які потребували тривалого часу. У моєму випадку той, що співвідносився з попередженнями в консолі, був із файлу, завантаженого розширенням AdBlock, але це може бути щось інше у вашому випадку.

Перевірте ці файли та спробуйте визначити, чи це код якогось розширення чи ваш. (Якщо це ваше, значить, ви знайшли джерело вашої проблеми.)


Ні, у мене немає AdBlock, і я все одно отримую його в консолі.
Микола Стоякович

Спробуйте проаналізувати це на вкладці «Ефективність» та шукайте джерело функцій, які працюють довго. Це може бути що завгодно, але це потенційний спосіб визначити джерело проблеми.
Метт Леонович

6

Подивіться на консоль Chrome на вкладці Мережа та знайдіть сценарії, які завантажуються найдовше.

У моєму випадку був набір кутових додань до скриптів, які я включив, але ще не використовував у додатку:

<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, які завантажувались більше часу, ніж час, визначений помилкою "Довго запуск".

Усі ці файли працюють на інших моїх веб-сайтах без помилок, але я отримував цю помилку "Довго виконання" у новому веб-додатку, який майже не мав функціоналу. Помилка припинилась відразу після видалення.

Я найкраще здогадуюсь, що ці кутові додатки шукали рекурсивно в більш глибокі ділянки 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 мс: Повторний перерахунок макета профілів хромової продуктивності

Зараз:

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
});

Після вставки цього трюкового коду всі попереджувальні повідомлення відпадають.



2

Якщо ви використовуєте Chrome Canary (або бета-версію), просто встановіть опцію "Сховати порушення".

Сховати порушення в прапорці консолі Chrome 56


1

Це помилка порушення від Google Chrome, яка показує, коли Verboseввімкнено рівень реєстрації.

Приклад повідомлення про помилку:

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

Пояснення:

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

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

І це посилання, яке Google Chrome надає у профілі продуктивності, на профілях компонування (області мав) для отримання додаткової інформації про попередження.


0

Додаючи сюди мою інформацію, оскільки ця тема стала питанням "перейти" до теми.

Моя проблема була в додатку Material-UI (на ранніх стадіях)

  • розміщення постачальника користувальницьких тем було причиною

коли я робив деякі обчислення примусового візуалізації сторінки (один компонент, "відображення результатів", залежить від того, що встановлено в інших, "розділи введення").

Все було добре, поки я не оновив "стан", який змушує "компонент результатів" відновлюватись. Основне питання тут полягало в тому, що я мав тему material-ui ( https://material-ui.com/customization/theming/#a-note-on-performance ) у тому ж рендері (App.js / return ..) як "компонент результатів", SummaryAppBarPure

Рішення полягало в тому, щоб підняти ThemeProvider на один рівень вгору (Index.js) і загортати компонент додатка тут, таким чином, не змушуючи ThemeProvider робити перерахунок та малювати / макет / поновлювати.

раніше

в 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 />
//...

-2

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

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

Я пропоную використовувати a setTimeoutдля вирішення проблеми.

Це не дуже важливо, але я повторюю, що проблема виникає, коли ви викликаєте функцію кілька разів, а не тоді, коли функція займає більше 50 мс. Я думаю, ви помиляєтесь у своїх відповідях.

  1. Вимкніть 1-на-1 дзвінки та перезавантажте код, щоб побачити, чи все-таки видає помилку.
  2. Якщо другий сценарій викликає помилку, використовуйте, setTimeOutвиходячи з тривалості порушення.

Це не рішення. Це пропозиція краще залишити коментарем до початкового питання.
Пол-Себастьян Маноле

-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 багатослівний.
Рішення підходу


5
Лише порада: Ваша відповідь не має нічого спільного з питаннями. Або виправте свою відповідь або видаліть її. Питання було "чому на консолі браузера Chrome відображається попередження про порушення". Відповідь полягає в тому, що це функція в новіших браузерах Chrome, де вона попереджає про те, що веб-сторінка викликає надмірне оновлення браузера під час виконання JS. Для отримання додаткової інформації зверніться до цього ресурсу від Google .
Пол-Себастьян Маноле
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.