Qual è la ragione per cui setTimeout di JavaScript è così impreciso?

Ho ottenuto questo codice qui:

var date = new Date(); setTimeout(function(e) { var currentDate = new Date(); if(currentDate - date >= 1000) { console.log(currentDate, date); console.log(currentDate-date); } else { console.log("It was less than a second!"); console.log(currentDate-date); } }, 1000); 

Nel mio computer, viene sempre eseguito correttamente, con 1000 nell’output della console. Interessato in altri computer, lo stesso codice, il callback di timeout inizia in meno di un secondo e la differenza di currentDate - date è compresa tra 980 e 998.

Conosco l’esistenza di librerie che risolvono questa inaccuratezza (ad esempio, Tock ).

Fondamentalmente, la mia domanda è: quali sono le ragioni perché setTimeout non si setTimeout in un dato ritardo? Potrebbe essere il computer che è troppo lento e il browser tenta automaticamente di adattarsi alla lentezza e spara l’evento prima?

PS: Ecco uno screenshot del codice e i risultati eseguiti nella console JavaScript di Chrome:

Inserisci qui la descrizione dell'immagine

Non dovrebbe essere particolarmente accurato. Ci sono una serie di fattori che limitano quanto presto il browser può eseguire il codice; citando da MDN :

Oltre a “clamping”, il timeout può anche essere triggersto più tardi quando la pagina (o il SO / browser stesso) è occupata con altre attività.

In altre parole, il modo in cui setTimeout viene solitamente implementato, è pensato per essere eseguito dopo un determinato ritardo e una volta che il thread del browser è libero di eseguirlo.

Tuttavia, diversi browser potrebbero implementarlo in modi diversi. Ecco alcuni test che ho fatto:

 var date = new Date(); setTimeout(function(e) { var currentDate = new Date(); console.log(currentDate-date); }, 1000); // Browser Test1 Test2 Test3 Test4 // Chrome 998 1014 998 998 // Firefox 1000 1001 1047 1000 // IE 11 1006 1013 1007 1005 

Forse le <1000 volte da Chrome potrebbero essere attribuite a imprecisioni nel tipo Date , o forse potrebbe essere che Chrome usi una strategia diversa per decidere quando eseguire il codice, forse sta tentando di adattarlo alla fascia oraria più vicina, anche se il ritardo di timeout non è ancora completato.

In breve, non dovresti usare setTimeout se ti aspetti un timing affidabile, coerente, al millisecondo.

In generale, i programmi per computer sono altamente inaffidabili quando si tenta di eseguire le cose con una precisione superiore a 50 ms. La ragione di questo è che anche su un processore iperthreaded ottupore il sistema operativo è in genere la giocoleria di diverse centinaia di processi e thread, a volte migliaia o più. Il sistema operativo fa funzionare tutto il multitasking pianificandoli tutti per ottenere una porzione di tempo CPU uno dopo l’altro, il che significa che ottengono “pochi millisecondi di tempo al massimo per fare le loro cose”.

Implicita vuol dire che se imposti un timeout per 1000 ms, le probabilità sono ben lontane dal fatto che l’attuale processo del browser non sarà nemmeno in esecuzione in quel momento, quindi è perfettamente normale che il browser non noti fino al 1005, 1010 o anche 1050 millisecondi che dovrebbe eseguire il callback specificato.

Di solito questo non è un problema, succede, ed è raramente della massima importanza. Se lo è, tutti i sistemi operativi forniscono timer a livello kernel che sono molto più precisi di 1 ms e consentono a uno sviluppatore di eseguire codice esattamente nel momento giusto. Tuttavia, JavaScript, in quanto ambiente pesantemente in modalità sandbox, non ha accesso agli oggetti del kernel in questo modo, e i browser evitano di utilizzarli poiché potrebbe teoricamente consentire a qualcuno di attaccare la stabilità del sistema operativo all’interno di una pagina Web, costruendo attentamente il codice che affama gli altri infila inondandolo con un sacco di timer pericolosi.

Per quanto riguarda il motivo per cui il test produce 980 non sono sicuro – ciò dipenderebbe esattamente da quale browser si sta utilizzando e da quale motore JavaScript. Posso comunque capire completamente se il browser si limita a correggere manualmente un po ‘verso il basso per il carico e / o la velocità del sistema, assicurando che “in media il ritardo è ancora sul tempo corretto” – avrebbe molto senso dal principio di sandboxing al solo approssimare la quantità di tempo richiesta senza gravare potenzialmente sul resto del sistema.

Qualcuno, per favore, correggimi se sto interpretando male queste informazioni:

Secondo un post di John Resig sull’inaccuratezza dei test delle prestazioni su piattaforms (enfasi sul mio)

Con i tempi di sistema costantemente arrotondati all’ultima query ( ciascuno di circa 15 ms ), la qualità dei risultati delle prestazioni è seriamente compromise.

Quindi c’è un fudge fino a 15 ms su entrambe le estremità quando si confronta con l’ora del sistema.