Violazione L'attività JavaScript a esecuzione prolungata ha richiesto xx ms


331

Di recente, ho ricevuto questo tipo di avviso e questa è la prima volta che lo ricevo:

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

Sto lavorando a un progetto di gruppo e non ho idea da dove provenga. Questo non è mai successo prima. Improvvisamente, è apparso quando qualcun altro è stato coinvolto nel progetto. Come trovo quale file / funzione causa questo avviso? Ho cercato la risposta, ma soprattutto sulla soluzione su come risolverla. Non riesco a risolverlo se non riesco nemmeno a trovare l'origine del problema.

In questo caso, l'avviso appare solo su Chrome. Ho provato a utilizzare Edge, ma non ho ricevuto avvisi simili e non l'ho ancora testato su Firefox.

Ricevo anche l'errore da jquery.min.js:

[Violation] Handler took 231ms of runtime (50ms allowed)            jquery.min.js:2

Dove vedi questo avviso? Non dici in quale ambiente stai lavorando. Supponendo un browser, ma quale ecc.?
Sami Kuhmonen,

3
@SamiKuhmonen mi dispiace per questo, ho aggiornato la mia domanda. ho usato Chrome. non ho riscontrato errori simili su Edge.
procuratore

8
Volevo solo aggiungere che questo messaggio di avviso, introdotto alla fine del 2016, potrebbe anche apparire a causa di eventuali estensioni che potresti aver installato in Chrome. È facile verificarlo testando in modalità privata.
Fer,

1
Facendo clic sul collegamento a destra, indicando lo script in cui si verificano le violazioni, ti porterà al punto nel codice in cui si verifica.
bluehipy,

Sto usando Ionic 4 (Angular 8), il mio codice stava funzionando bene, improvvisamente questo tipo di violazione ha iniziato a venire - non ci sono dati visualizzati nella mia lista ora?
Kapil Raghuwanshi,

Risposte:


278

Aggiornamento : Chrome 58+ ha nascosto questi e altri messaggi di debug per impostazione predefinita. Per visualizzarli, fai clic sulla freccia accanto a "Informazioni" e seleziona "Dettagli".

Chrome 57 ha attivato "nascondi violazioni" per impostazione predefinita. Per riattivarli è necessario abilitare i filtri e deselezionare la casella "nascondi violazioni".

improvvisamente appare quando qualcun altro è coinvolto nel progetto

Penso che sia più probabile che tu abbia aggiornato a Chrome 56. Questo avviso è una nuova meravigliosa funzionalità, a mio avviso, ti preghiamo di disattivarlo solo se sei disperato e il tuo valutatore ti toglierà i voti. I problemi sottostanti sono presenti negli altri browser, ma i browser non ti stanno dicendo che c'è un problema. Il biglietto Chromium è qui ma non c'è davvero nessuna discussione interessante su di esso.

Questi messaggi sono avvertimenti invece di errori perché non causeranno problemi importanti. Potrebbe causare la caduta dei frame o comunque un'esperienza meno fluida.

Vale comunque la pena indagare e correggere per migliorare la qualità della tua applicazione. Il modo per farlo è prestando attenzione alle circostanze in cui appaiono i messaggi e facendo test delle prestazioni per restringere il luogo in cui si sta verificando il problema. Il modo più semplice per avviare il test delle prestazioni è inserire un codice come questo:

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

    // Do the normal stuff for this function

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

Se vuoi essere più avanzato, puoi anche utilizzare il profiler di Chrome o utilizzare una libreria di benchmarking come questa .

Una volta trovato del codice che impiega molto tempo (50ms è la soglia di Chrome), hai un paio di opzioni:

  1. Elimina alcune / tutte quelle attività che potrebbero non essere necessarie
  2. Scopri come svolgere la stessa attività più velocemente
  3. Dividi il codice in più passaggi asincroni

(1) e (2) possono essere difficili o impossibili, ma a volte è davvero facile e dovrebbero essere i tuoi primi tentativi. Se necessario, dovrebbe essere sempre possibile farlo (3). Per fare questo userai qualcosa come:

setTimeout(functionToRunVerySoonButNotNow);

o

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

Puoi leggere di più sulla natura asincrona di JavaScript qui .


16
Solo un suggerimento, invece di usare performance.now(), potresti usare console.time( developer.mozilla.org/en-US/docs/Web/API/Console/time ) console.time('UniquetLabelName') ....code here.... console.timeEnd('UniqueLabelName')
denislexic

@denislexic credo di si. Non sono sicuro del valore che aggiunge davvero però. Direi che è più prezioso conoscere l'operazione sottostante per ottenere l'ora corrente e approfondirla.
voltrevo

34
Ottima risposta, voltrevo! La mia domanda è: se un codice come questo è una violazione, di cosa si tratta esattamente? Google deve applicare una sorta di standard, ma lo standard è documentato pubblicamente ovunque?
Bungler,

1
@Bungler Non so, vorrei sapere se c'è qualche linea guida a cui si riferisce pure.
voltrevo

4
@Bungler Posso solo supporre che stia dicendo che il codice che sta animando viola il fatto di fornire almeno un frame di 60 al secondo e quindi di offrire un'esperienza utente scadente. .
user895400

90

Questi sono solo avvertimenti come tutti menzionati. Tuttavia, se sei interessato a risolverli (cosa che dovresti), allora devi identificare prima cosa sta causando l'avvertimento. Non esiste un motivo per cui è possibile ricevere un avviso di riflusso della forza. Qualcuno ha creato un elenco per alcune possibili opzioni. Puoi seguire la discussione per ulteriori informazioni.
Ecco l'essenza dei possibili motivi:

Ciò che impone layout / reflow

Tutte le proprietà o i metodi seguenti, quando richiesti / chiamati in JavaScript, attiveranno il browser per calcolare in modo sincrono lo stile e il layout *. Questo è anche chiamato reflow o thrashing del layout ed è un collo di bottiglia delle prestazioni comune.

Elemento

Metriche della scatola
  • elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight,elem.offsetParent
  • elem.clientLeft, elem.clientTop, elem.clientWidth,elem.clientHeight
  • elem.getClientRects(), elem.getBoundingClientRect()
Scorri roba
  • elem.scrollBy(), elem.scrollTo()
  • elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()
  • elem.scrollWidth, elem.scrollHeight
  • elem.scrollLeft, elem.scrollTopinoltre, impostandoli
Messa a fuoco
  • elem.focus() può attivare un doppio layout forzato ( sorgente )
Anche…
  • elem.computedRole, elem.computedName
  • elem.innerText( fonte )

getComputedStyle

window.getComputedStyle()forzerà in genere il ricalcolo dello stile ( sorgente )

window.getComputedStyle() imporrà anche il layout, se si verifica una delle seguenti condizioni:

  1. L'elemento si trova in un albero delle ombre
  2. Esistono media query (relative a viewport). Specificamente, una delle seguenti: ( sorgente ) * min-width, min-height, max-width, max-height, width, height * aspect-ratio, min-aspect-ratio,max-aspect-ratio
    • device-pixel-ratio, resolution,orientation
  3. La proprietà richiesta è una delle seguenti: ( fonte )
    • height, width * top, right, bottom, left * margin[ -top, -right, -bottom, -left, O scorciatoia ] solo se il margine è fisso. * padding[ -top, -right, -bottom, -left, O scorciatoia ] solo se l'imbottitura è fisso. * transform, transform-origin, perspective-origin * translate, rotate, scale * webkit-filter, backdrop-filter * motion-path, motion-offset, motion-rotation * x, y, rx,ry

finestra

  • window.scrollX, window.scrollY
  • window.innerHeight, window.innerWidth
  • window.getMatchedCSSRules() forza solo lo stile

Forme

  • inputElem.focus()
  • inputElem.select(), textareaElem.select()( fonte )

Eventi del mouse

  • mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY ( Fonte )

documento

  • doc.scrollingElement forza solo lo stile

Gamma

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

SVG

contenteditable

  • Un sacco di cose, ... tra cui la copia di un'immagine negli appunti ( fonte )

Controlla di più qui .

Inoltre, ecco il codice sorgente di Chromium dal problema originale e una discussione su un'API delle prestazioni per gli avvisi.


Modifica: c'è anche un articolo su come ridurre al minimo il ridimensionamento del layout su PageSpeed ​​Insight di Google . Spiega cos'è il reflow del browser:

Reflow è il nome del processo del browser Web per il ricalcolo delle posizioni e delle geometrie degli elementi nel documento, allo scopo di eseguire nuovamente il rendering di parte o di tutto il documento. Poiché il reflow è un'operazione di blocco dell'utente nel browser, è utile che gli sviluppatori comprendano come migliorare il tempo di reflow e anche comprendere gli effetti di varie proprietà del documento (profondità DOM, efficienza delle regole CSS, diversi tipi di modifiche di stile) sul reflow tempo. A volte il reflow di un singolo elemento nel documento può richiedere il reflow dei suoi elementi padre e anche di tutti gli elementi che lo seguono.

Inoltre, spiega come minimizzarlo:

  1. Ridurre la profondità DOM non necessaria. Le modifiche a un livello nella struttura DOM possono causare modifiche a tutti i livelli della struttura, fino alla radice e fino ai figli del nodo modificato. Ciò porta a dedicare più tempo all'esecuzione del reflow.
  2. Ridurre al minimo le regole CSS e rimuovere le regole CSS non utilizzate.
  3. Se si apportano modifiche complesse al rendering come le animazioni, farlo fuori dal flusso. Usa la posizione assoluta o la posizione fissa per ottenere questo risultato.
  4. Evita selettori CSS complessi non necessari - in particolare i selettori discendenti - che richiedono più potenza della CPU per eseguire la corrispondenza dei selettori.

1
Ulteriori informazioni: il codice sorgente di Chromium dal problema originale e una discussione su un'API delle prestazioni per gli avvisi.
robocat,

1
Secondo quanto sopra, la semplice lettura di element.scrollTop attiva un riflusso. Questo mi sembra un fenomeno controintuitivo. Posso capire perché l' impostazione di element.scrollTop attiverà un riflusso, ma semplicemente la lettura del suo valore? Qualcuno può spiegare ulteriormente perché questo è il caso, se davvero è così?
David Edwards,

29

Un paio di idee:

  • Rimuovi metà del codice (magari tramite un commento).

    • Il problema è ancora lì? Fantastico, hai ristretto le possibilità! Ripetere.

    • Il problema non è lì? Ok, guarda la metà che hai commentato!

  • Stai utilizzando un sistema di controllo versione (ad es. Git)? In tal caso, git checkoutalcuni dei tuoi impegni più recenti. Quando è stato introdotto il problema? Guarda il commit per vedere esattamente quale codice è cambiato quando è arrivato il problema.


La ringrazio per la risposta. ho rimosso metà e ho persino escluso il mio file .js principale dal progetto. in qualche modo l'errore si è ancora verificato. questo è il motivo per cui sono così frustrante. e sì, sto usando git. ho appena capito questo errore oggi. ci sono stati molti impegni da quando questo è diventato un progetto di gruppo. potrebbe fare un controllo approfondito. grazie ancora per le idee.
procuratore

@procatmer usa la stessa strategia per trovare il commit git. Ad esempio, se avessi 10 commit (A, B, C, D, E, F, G, H, I, J) dove A era il più vecchio, vedrei git checkout Ese il problema esiste già. In caso affermativo, continuerò a cercare il problema nella prima metà degli commit. Altrimenti, cerco il problema nella seconda metà.
therobinkim,

1
@procatmer Inoltre, se hai omesso il tuo .jsfile principale e il problema persiste ... potrebbe essere una libreria che hai portato tramite un <script src="...">tag! Forse qualcosa di cui non vale la pena preoccuparsi (soprattutto perché è solo un avvertimento)?
therobinkim,

1
ho finalmente trovato dove si trova il problema. ho usato la tua seconda idea per tenere traccia delle modifiche. e sì, il problema proviene da un .jsfile esterno . a quanto pare, importa. rallenta il mio sito abbastanza significativo. comunque grazie ancora per le tue risposte e idee.
procuratore

2
Puoi usare git bisect per applicare la ricerca binaria. Penso che sia solo allo scopo di trovare bug.
Pietrovismara,

12

Per identificare l'origine del problema, esegui l'applicazione e registrala nella scheda Prestazioni di Chrome .

Lì puoi controllare varie funzioni che hanno richiesto molto tempo per essere eseguite. Nel mio caso, quello correlato agli avvisi in console proveniva da un file che era stato caricato dall'estensione AdBlock, ma questo potrebbe essere qualcos'altro nel tuo caso.

Controlla questi file e prova a identificare se questo è un codice di estensione o il tuo. (Se è tuo, hai trovato la fonte del tuo problema.)


No, non ho AdBlock e lo ottengo ancora nella console.
Nikola Stojaković,

Prova ad analizzarlo con la scheda Prestazioni e cerca l'origine delle funzioni che funzionano da molto tempo. Questo potrebbe essere qualsiasi cosa, ma questo è un potenziale modo per identificare l'origine del problema.
Matt Leonowicz,

6

Cerca nella console di Chrome nella scheda Rete e trova gli script che impiegano più tempo a caricarsi.

Nel mio caso c'erano un set di script angolari aggiuntivi che avevo incluso ma non ancora utilizzato nell'app:

<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>

Questi erano gli unici file JavaScript che impiegavano più tempo a caricarsi rispetto al tempo specificato dall'errore "Attività in corso".

Tutti questi file vengono eseguiti su altri miei siti Web senza errori generati, ma ho visualizzato questo errore "Attività in corso" su una nuova app Web che a malapena aveva funzionalità. L'errore si è interrotto immediatamente dopo la rimozione.

La mia ipotesi migliore è che questi componenti aggiuntivi angolari stessero esaminando ricorsivamente sezioni sempre più profonde del DOM per i loro tag di inizio - trovando nessuno, dovevano attraversare l'intero DOM prima di uscire, che impiegava più tempo di quanto previsto da Chrome - quindi l'avvertimento.


6

Ho trovato la radice di questo messaggio nel mio codice, che ha cercato, nascosto o mostrato nodi (offline). Questo era il mio codice:

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

La scheda delle prestazioni (profiler) mostra l'evento che richiede circa 60 ms: Riflusso di ricalcolo del layout del profiler delle prestazioni di cromo

Adesso:

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

La scheda delle prestazioni (profiler) ora mostra l'evento che richiede circa 1 ms: Profilatore di cromo scuro

E sento che la ricerca ora funziona più velocemente (229 nodi).


3
In breve, ricevendo la violazione, sei stato in grado di ottimizzare il tuo codice e ora funziona meglio.
Utente che non è un utente

3

Ho trovato una soluzione nel codice sorgente di Apache Cordova. Si implementano così:

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

Implementazione semplice, ma intelligente.

Su Android 4.4, utilizzare Promise. Per i browser meno recenti, utilizzaresetTimeout()


Uso:

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

Dopo aver inserito questo codice trucco, tutti i messaggi di avviso scompaiono.



2

Se stai utilizzando Chrome Canary (o Beta), seleziona l'opzione "Nascondi violazioni".

Casella di controllo Nascondi violazioni nella console di Chrome 56


1

Questo è un errore di violazione di Google Chrome che mostra quando Verbose livello di registrazione è abilitato.

Esempio di messaggio di errore:

screenshot dell'avviso

Spiegazione:

Reflow è il nome del processo del browser Web per il ricalcolo delle posizioni e delle geometrie degli elementi nel documento, allo scopo di eseguire nuovamente il rendering di parte o di tutto il documento. Poiché il reflow è un'operazione di blocco dell'utente nel browser, è utile che gli sviluppatori comprendano come migliorare il tempo di reflow e anche comprendere gli effetti di varie proprietà del documento (profondità DOM, efficienza delle regole CSS, diversi tipi di modifiche di stile) sul reflow tempo. A volte il reflow di un singolo elemento nel documento può richiedere il reflow dei suoi elementi padre e anche di tutti gli elementi che lo seguono.

Articolo originale: Riduzione al minimo del riflusso del browser di Lindsey Simon, sviluppatore UX, pubblicato su developers.google.com.

E questo è il link che Google Chrome ti offre nel profiler delle prestazioni, sui profili di layout (le aree malva), per maggiori informazioni sull'avvertimento.


0

Aggiungendo le mie intuizioni qui come questa discussione era la domanda "vai a" stackoverflow sull'argomento.

Il mio problema era in un'app Materiale-UI (fasi iniziali)

  • il posizionamento del fornitore del tema personalizzato era la causa

quando ho fatto alcuni calcoli forzando il rendering della pagina (un componente, "visualizza risultati", dipende da ciò che è impostato in altri, "sezioni di input").

Tutto è andato bene fino a quando non ho aggiornato lo "stato" che forza il "componente dei risultati" a renderizzare. Il problema principale qui era che avevo un tema material-ui ( https://material-ui.com/customization/theming/#a-note-on-performance ) nello stesso renderer (App.js / return ..) come "componente dei risultati", SummaryAppBarPure

Soluzione era quella di elevare il ThemeProvider di un livello superiore (Index.js) e di racchiudere qui il componente App, quindi non forzare il ThemeProvider a ricalcolare e disegnare / layout / reflow.

prima

in App.js:

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

        <SummaryAppBarPure
//...

in index.js

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

dopo

in App.js:

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

        <SummaryAppBarPure
//...

in index.js

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

-2

Il riflusso forzato si verifica spesso quando si dispone di una funzione chiamata più volte prima della fine dell'esecuzione.

Ad esempio, potresti avere il problema su uno smartphone, ma non su un browser classico.

Suggerisco di usare a setTimeoutper risolvere il problema.

Questo non è molto importante, ma ripeto, il problema sorge quando si chiama una funzione più volte e non quando la funzione richiede più di 50 ms. Penso che ti sbagli nelle tue risposte.

  1. Disattiva le chiamate 1 per 1 e ricarica il codice per vedere se produce ancora l'errore.
  2. Se un secondo script provoca l'errore, utilizzare un setTimeOutbasato sulla durata della violazione.

Questa non è una soluzione. È un suggerimento che è meglio lasciare come commento alla domanda originale.
Paul-Sebastian Manole

-6

Questo non è un errore, ma semplicemente un messaggio. Per eseguire questa modifica del messaggio
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">(esempio)
in
<!DOCTYPE html>(la fonte di Firefox si aspetta questo)

Il messaggio è stato mostrato in Google Chrome 74 e Opera 60. Dopo averlo modificato era chiaro, 0 dettagliato.
Un approccio di soluzione


5
Solo qualche consiglio: la tua risposta non ha nulla a che fare con le domande. Correggi la tua risposta o rimuovila. La domanda era "perché la console del browser Chrome mostra un avviso di violazione". La risposta è che è una funzionalità dei browser Chrome più recenti in cui ti avvisa se la pagina web provoca un eccessivo ridisposizione del browser durante l'esecuzione di JS. Per ulteriori informazioni, consultare questa risorsa di Google .
Paul-Sebastian Manole
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.