Что именно является аргументом в обратном вызове requestAnimationFrame?

#javascript #html #canvas #requestanimationframe

Вопрос:

Просто запустите это:

 function requestAndDraw() {
  requestAnimationFrame((t) => {
    console.log(`T: ${t} P.now:${performance.now()}`);
  });
}

intId = setInterval(requestAndDraw, 20);
setTimeout(() => clearInterval(intId), 1000); 

Он печатает что-то вроде этого:

 T: 1164.656 P.now:1176.300000000083
 

Интересно, в чем причина разницы в 12 мс?
Потребовалось некоторое время, чтобы выполнить некоторые другие обратные вызовы, зарегистрированные в requestAnimationFrame ? Это просто какие-то накладные расходы, связанные с интерпретацией js? или что? 12 мс-это не то, что я бы выбросил в этом контексте.

Комментарии:

1. Он регистрирует для меня точно такие же значения (я только что отредактировал ваш вопрос с помощью запущенной демо-версии, нажмите «запустить фрагмент кода»). Кроме того, вопрос в названии вашего вопроса и вопрос в тексте вашего вопроса не совпадают, вы задаете здесь два разных вопроса. Ответ на название может быть таким: developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp

2. В каком браузере это происходит? Я не получаю такого же поведения, разница только ~1 мс в Chrome v66

3. Что касается моего браузера — Chrome v66/Ubuntu 18.04. Так что это как-то странно. Что касается вопроса — в следующий раз я буду менее общим в названии вопроса. И я не знал о функции «Запустить фрагмент кода» — спасибо за редактирование. Также… Когда я запускаю этот фрагмент, я получаю ту же разницу в 1 мс, но когда я копирую-вставляю его в консоль DevTools, я получаю эти 12 мс — похоже, это причина для другого вопроса) Хотя не знаю, как это назвать)

Ответ №1:

Аргумент представляет собой метку времени DOMHighResTimeStamp, точно так же, как и то, что Performance.now должен возвращать, даже если это не тот, который возвращается этим методом.


Итак, сначала позвольте объяснить разницу в точности, которую вы видите: недавно в большинстве процессоров была обнаружена серьезная проблема безопасности, известная под именами Meltdown и Spectre.
Эти атаки могут быть совершены из веб-браузера, но для этого требуется метка DOMHighResTimeStamp, возвращаемая Performance.now. Быстрое исправление / «смягчение», как обнаружил Firefox, заключалось в снижении разрешения этой метки времени. (см. Подробнее).

Поскольку обратные вызовы requestAnimationFrame запланированы для запуска следующего кадра (т. е. ~16 мс после предыдущего вызова), метка DOMHighResTimeStamp, переданная из этого метода, не требует этого смягчения, следовательно, у вас все еще есть полная точность в этой метке DOMHighResTimeStamp.


Теперь, чтобы ответить на вопрос заголовка, метка DOMHighResTimeStamp, переданная в requestAnimationFrame обратных вызовах, представляет время вызова стека всех обратных вызовов для этого кадра.
Действительно, requestAnimationFrame хранит параметр обратного вызова только в стеке, и этот стек затем вызывается при появлении кадра (непосредственно перед следующей операцией рисования).
Это означает, что все обратные вызовы для одного и того же кадра будут иметь одну и ту же метку времени, независимо от того, сколько времени занял предыдущий обратный вызов.

 function long(time){
  var now = performance.now();
  console.log('long');
  console.log({
    'rAF time': time,
    'Performance time': now,
    'diff': (now - time)   'ms'
  });
  console.log('________________________');
  // wait 50ms
  while(performance.now() - time < 50) {
  }
}
function short(time) {
  var now = performance.now();
  console.log('short');
  console.log({
    'rAF time': time,
    'Performance time': now,
    'diff': (now - time)   'ms'
  });
}
// our two functions will be stacked together to fire in the same frame
requestAnimationFrame(long);
requestAnimationFrame(short);