2016-07-13 1 views
3

Hypothèses: rAF now le temps est calculé au moment où l'ensemble des rappels sont tous déclenchés. Par conséquent, tout blocage qui se produit avant le premier rappel de cette trame n'affecte pas le RAF now et il est précis - au moins pour ce premier rappel.requestAnimationFrame [maintenant] vs performance.now() écart de temps

Toute mesure performance.now() effectuée avant le déclenchement d'un ensemble rAF doit être antérieure à la valeur now.

Test: Enregistrer before (un temps de référence avant quoi que ce soit se produit). Définir le prochain RAF. Comparez rAF now et réel performance.now() à before pour voir comment ils sont différents.

Résultats attendus:

var before = performance.now(), frames = ["with blocking", "with no blocking"], calls = 0; 
 
requestAnimationFrame(function frame(rAFnow) { 
 
    var actual = performance.now(); 
 
    console.log("frame " + (calls + 1) + " " + frames[calls] + ":"); 
 
    console.log("before frame -> rAF now: " + (rAFnow - before)); 
 
    console.log("before frame -> rAF actual: " + (actual - before)); 
 

 
    if (++calls < frames.length) { before = actual; requestAnimationFrame(frame); } 
 
}); 
 

 
// blocking 
 
for (var i = 0, l = 0; i < 10000000; i++) { 
 
    l += i; 
 
}

Observations: Quand il bloque avant que la trame commence, le temps rFA now est parfois incorrecte, même pour cette première image. Parfois, la première image now est en réalité une heure antérieure à l'heure enregistrée before.

Qu'il y bloque passe avant que le cadre ou non, tous si souvent le temps en cadre rAFnow sera plus tôt que le temps de pré-cadre before --Même quand je configuration après la Royal Air Force je prends ma première mesure . Cela peut aussi arriver sans aucun blocage, même si c'est plus rare.

(je reçois une erreur de synchronisation sur la première image de blocage la plupart du temps. Obtenir une question sur les autres est plus rare, mais ne se produit de temps en temps si vous essayez d'exécuter quelques fois.)

Avec plus vaste test, j'ai trouvé des mauvais moments avec blocage avant rappel: 1% à partir de 100 images, pas de blocage: 0.21645021645021645% à partir de ~ 400 images, apparemment causé par l'ouverture d'une fenêtre ou une autre action potentiellement gourmande en CPU par l'utilisateur.

Donc, c'est assez rare, mais le problème est que cela ne devrait pas arriver du tout. Si vous voulez faire des choses utiles avec eux, simuler le temps, l'animation, etc., alors vous avez besoin de ces moments pour avoir du sens.

J'ai pris en compte ce que les gens ont dit, mais peut-être que je ne comprends toujours pas comment les choses fonctionnent. Si tout cela est conforme à la spécification, j'adorerais un code de pseudo-code pour le consolider dans mon esprit.

Et surtout, si quelqu'un a des suggestions pour contourner ces problèmes, ce serait génial. La seule chose que je peux penser est de prendre ma propre mesure performance.now() chaque image et l'utiliser - mais il semble un peu inutile, l'ayant effectivement exécuté deux fois par trame, en plus des événements déclenchés et ainsi de suite.

+0

J'ai ajouté un état d'arrêt à votre extrait. N'hésitez pas à revenir en arrière, mais de cette façon le code se termine à un moment donné :). –

+0

Non, c'est cool. Merci, @MikeMcCaughan. – Whothehellisthat

Répondre

5

L'horodatage transmis au rappel requestAnimationFrame() correspond à l'heure du début de la trame d'animation. Les rappels multiples invoqués pendant la même trame reçoivent tous le même horodatage. Ainsi, il serait vraiment étrange si performance.now() retourné un temps avant la valeur du paramètre, mais pas vraiment bizarre pour que ce soit après que.

Here's the relevant specification:

Lorsque l'agent utilisateur est d'exécuter les callbacks cadre d'animation pour un document de document avec un horodatage maintenant, il doit exécuter les étapes suivantes:

  1. Si la valeur renvoyé par l'attribut caché de l'objet document est true, annulez ces étapes. [PAGE-VISIBILITY]

  2. Laisser les rappels être une liste des entrées dans la liste des rappels de trame d'animation du document, dans l'ordre dans lequel ils ont été ajoutés à la liste.

  3. Définissez la liste des rappels d'image d'animation du document dans la liste vide.

  4. Pour chaque entrée callbacks, dans l'ordre: appeler la fonction de rappel IDL Web, en passant maintenant comme seul argument, et si une exception est levée, signaler l'exception.

Vous avez enregistré un rappel (disons un seul) pour la trame suivante d'animation. Tick ​​tick, BOOM, le temps pour cette image d'animation se produise:

  1. Le moteur d'exécution JavaScript fait une note du temps et des étiquettes qui maintenant.
  2. Le moteur d'exécution effectue une copie temporaire de la liste des rappels de cadres d'animation enregistrés et efface la liste actuelle (de sorte qu'ils ne sont pas accidentellement invoqués si le cadre d'animation suivant prend trop de temps).
  3. La liste n'a qu'une seule chose: votre callback. Le système appelle cela avec maintenant comme paramètre.
  4. Votre rappel commence à s'exécuter. Peut-être que cela n'a jamais été exécuté auparavant, l'optimiseur JavaScript devra peut-être faire un peu de travail. Ou peut-être que le système d'exploitation bascule les threads vers un autre processus système, comme le démarrage d'un vidage de tampon de disque ou la gestion d'un trafic réseau, ou l'une des dizaines d'autres choses.
  5. Oh, à droite, votre rappel. Le navigateur récupère le processeur et votre code de rappel commence à s'exécuter.
  6. Votre code appelle performance.now() et le compare à la valeur maintenant transmise en tant que paramètre.

Parce qu'un bref mais non ignorable de temps peut passer entre l'étape 1 et l'étape 6, la valeur de retour de performance.now() peut indiquer que quelques microsecondes, ou même plus de deux, se sont écoulés. C'est un comportement parfaitement normal.

+0

Êtes-vous en train de dire que _any_ callback, même ceux qui ne le sont pas, auront la même valeur performance.now()? Cela ne semble pas être le cas. Si vous voulez dire plusieurs rappels RAF, je n'utilise qu'un seul RAF. – Whothehellisthat

+0

J'ai fait des modifications pour mieux expliquer ce qui se passe. (Je n'étais pas sûr si vous seriez averti?) – Whothehellisthat

+0

@Whothehellisthat non non non. Lorsque l'événement se produit à l'intérieur de l'exécution JavaScript qui déclenche le traitement des rappels pour une trame d'animation, l'horodatage est noté ** une fois ** au début de ce processus. Chaque callback enregistré par 'requestAnimationFrame()' est passé exactement la même valeur de temps. Cela n'a vraiment rien à voir avec les appels ultérieurs à 'performance.now()', sauf qu'ils vont évidemment tous se produire * après * ce temps initial. – Pointy

2

j'ai rencontré la même question sur le chrome, où les appels à performance.now() renverrait une valeur supérieure à la valeur now passé dans callbacks par la suite par window.requestAnimationFrame()

Ma solution était de mettre le before en utilisant le now passé à la rappel dans le premier window.requestAnimationFrame()plutôt que performance.now(). Il semble que l'utilisation seulement une des deux fonctions pour mesurer le temps garantit des valeurs progressives. J'espère que cela aidera quelqu'un d'autre souffrant de ce bug.