Programing

setTimeout보다 Javascript 타이머를 만드는 더 정확한 방법이 있습니까?

crosscheck 2020. 11. 11. 08:02
반응형

setTimeout보다 Javascript 타이머를 만드는 더 정확한 방법이 있습니까?


항상 나를 괴롭힌 것은 setTimeout()Javascript 방법 이 얼마나 예측할 수 없는지 입니다.

내 경험상 타이머는 많은 상황에서 끔찍하게 부정확합니다. 부정확하다는 것은 실제 지연 시간이 250-500ms 정도 차이가 나는 것 같습니다. 엄청난 시간은 아니지만 UI 요소를 숨기거나 표시하는 데 사용할 때 시간이 눈에 띄게 눈에 띄게 표시 될 수 있습니다.

setTimeout()정확한 성능 을 보장하기 위해 수행 할 수있는 트릭이 있습니까 (외부 API에 의존하지 않고) 아니면 이것이 손실 된 원인입니까?


setTimeout()정확한 성능 을 보장하기 위해 수행 할 수있는 트릭이 있습니까 (외부 API에 의존하지 않고) 아니면 이것이 손실 된 원인입니까?

아니, 아니. 당신은 완벽하게 정확한 타이머에 가까운 것을 얻지 못할 것입니다 setTimeout()-브라우저는 그것을 위해 설정되지 않았습니다. 그러나 타이밍에 의존 할 필요는 없습니다. 대부분의 애니메이션 라이브러리는 몇 년 전에 이것을 알아 냈습니다.를 사용하여 콜백을 설정 setTimeout()했지만 (new Date()).milliseconds(또는 동등한) 값에 따라 수행해야 할 작업을 결정합니다 . 이를 통해 최신 브라우저에서 더 안정적인 타이머 지원을 활용하면서 이전 브라우저에서 적절하게 작동 할 수 있습니다.

또한 너무 많은 타이머를 사용하지 않아도됩니다 ! 이것은 중요합니다. 각 타이머는 콜백입니다. 각 콜백은 JS 코드를 실행합니다. JS 코드가 실행되는 동안 다른 콜백을 포함한 브라우저 이벤트가 지연되거나 삭제됩니다. 콜백이 완료되면 추가 콜백이 실행 기회를 얻기 위해 다른 브라우저 이벤트와 경쟁해야합니다. 따라서 해당 간격에 대해 모든 보류중인 작업을 처리하는 하나의 타이머는 간격이 일치하는 두 개의 타이머보다 더 잘 수행되며 (짧은 제한 시간의 경우) 시간 제한이 겹치는 두 개의 타이머보다 더 좋습니다!

요약 : setTimeout()"하나의 타이머 / 하나의 작업"설계를 구현 하는 사용 중지 하고 실시간 시계를 사용하여 UI 작업을 부드럽게합니다.


.

REF; http://www.sitepoint.com/creating-accurate-timers-in-javascript/

이 사이트는 나를 큰 규모로 구제했습니다.

시스템 시계를 사용하여 부정확 한 타이머를 보정 할 수 있습니다. 타이밍 함수를 일련의 setTimeout 호출 (각 인스턴스가 다음 인스턴스를 호출)로 실행하는 경우 정확하게 유지하기 위해해야 ​​할 일은 정확히 얼마나 부 정확한지 파악하고 다음 반복에서 그 차이를 빼는 것입니다.

var start = new Date().getTime(),  
    time = 0,  
    elapsed = '0.0';  
function instance()  
{  
    time += 100;  
    elapsed = Math.floor(time / 100) / 10;  
    if(Math.round(elapsed) == elapsed) { elapsed += '.0'; }  
    document.title = elapsed;  
    var diff = (new Date().getTime() - start) - time;  
    window.setTimeout(instance, (100 - diff));  
}  
window.setTimeout(instance, 100);  

이 방법은 드리프트를 최소화하고 부정확성을 90 % 이상 줄입니다.

내 문제가 해결 되었으니 도움이되기를 바랍니다


얼마 전에 비슷한 문제가 있었고 requestAnimationFrame매우 효과적으로 작동하는 performance.now ()와 결합하는 접근 방식을 생각해 냈습니다 .

이제 타이머를 소수점 이하 12 자리까지 정확하게 만들 수 있습니다.

    window.performance = window.performance || {};
    performance.now = (function() {
        return performance.now       ||
            performance.mozNow    ||
            performance.msNow     ||
            performance.oNow      ||
            performance.webkitNow ||
                function() {
                    //Doh! Crap browser!
                    return new Date().getTime(); 
                };
        })();

http://jsfiddle.net/CGWGreen/9pg9L/


shog9의 대답은 UI 애니메이션 / 이벤트에 대해 다음을 추가했지만 내가 말한 것과 거의 같습니다.

화면 위로 슬라이드해야하는 상자가있는 경우 아래로 확장 한 다음 해당 내용을 페이드 인해 야합니다. 세 이벤트를 모두 지연 시간을두고 분리하여 하나씩 실행하지 마십시오. 콜백을 사용하십시오. 첫 번째 이벤트가 슬라이딩 완료되면 확장기를 호출하고 완료되면 페이더를 호출합니다. jQuery는 쉽게 할 수 있으며 다른 라이브러리도 할 수 있다고 확신합니다.


지정된 간격으로 정확한 콜백을 받아야하는 경우 다음 요점이 도움이 될 수 있습니다.

https://gist.github.com/1185904


setTimeout()UI 스레드가 수행해야하는 모든 작업 (예 : 탭 업데이트 또는 장기 실행 스크립트 대화 상자 표시 안 함)을 따라 잡을 수 있도록 브라우저에 빠르게 양보 하는 데 사용 하는 경우 Efficient 라는 새 API가 있습니다. 스크립트 Yielding (일명) setImmediate()이 조금 더 효과적 일 수 있습니다.

setImmediate()와 매우 유사하게 작동 setTimeout()하지만 브라우저에 다른 작업이 없으면 즉시 실행될 수 있습니다. 당신이 사용하는 많은 경우 setTimeout(..., 16)setTimeout(..., 4)또는 setTimeout(..., 0), 당신은 단순히 당신을 대체 할 수있다 (당신이 긴 스크립트 실행 대화 상자를 보여 미결제 UI 스레드 작업을 실행하지 않도록 브라우저 원하는 즉) setTimeout()setImmediate()두 번째 (밀리 초) 인수를 포기을.

차이점 setImmediate()은 기본적으로 수율이라는 것입니다. 브라우저가 UI 스레드에서 작업을 수행해야하는 경우 (예 : 탭 업데이트) 콜백으로 돌아 가기 전에 수행합니다. 그러나 브라우저가 이미 작업을 모두 따라 잡은 경우에 지정된 콜백 setImmediate()은 기본적으로 지연없이 실행됩니다.

안타깝게도 현재 IE9 +에서만 지원됩니다 . 다른 브라우저 공급 업체의 일부 푸시 백이 있기 때문입니다 .

그래도 사용할 수 있는 좋은 polyfill이 있습니다. 만약 당신이 그것을 사용하고 싶고 다른 브라우저가 어떤 시점에서 그것을 구현하기를 희망한다면.

당신이 사용하는 경우 setTimeout()애니메이션 , requestAnimationFrame는 코드가에 동기화 모니터의 새로 고침 속도와 실행으로 당신의 최선의 방법이다.

당신이 사용하는 경우 setTimeout()느린 리듬에 , 예를 들어, 모든 300 밀리 초 후에는 user1213320이 시사하는 것과 유사한 솔루션을 사용할 수 있습니다, 당신은 마지막 타임 스탬프에서 타이머 달렸다 시간을 모니터링하고 지연을 보상 곳. 한 가지 개선 사항은 현재 시간에 대해 밀리 초 이상의 해상도를 얻는 대신 새로운 고해상도 시간 인터페이스 (일명 window.performance.now())를 사용할 수 있다는 것 Date.now()입니다.


목표 시간에 "크립 업"해야합니다. 약간의 시행 착오가 필요하지만 본질적으로.

시간 제한을 설정하여 필요한 시간 100ms 전에 완료하십시오.

시간 초과 처리기를 다음과 같이 만듭니다.

calculate_remaining_time
if remaining_time > 20ms // maybe as much as 50
  re-queue the handler for 10ms time
else
{
  while( remaining_time > 0 ) calculate_remaining_time;
  do_your_thing();
  re-queue the handler for 100ms before the next required time
}

그러나 while 루프는 여전히 다른 프로세스에 의해 중단 될 수 있으므로 여전히 완벽하지 않습니다.


다음은 Shog9의 제안을 시연하는 예입니다. 이렇게하면 jquery 진행률 표시 줄이 6 초에 걸쳐 매끄럽게 채워진 다음 채워지면 다른 페이지로 리디렉션됩니다.

var TOTAL_SEC = 6;
var FRAMES_PER_SEC = 60;
var percent = 0;
var startTime = new Date().getTime();

setTimeout(updateProgress, 1000 / FRAMES_PER_SEC);

function updateProgress() {
    var currentTime = new Date().getTime();

    // 1000 to convert to milliseconds, and 100 to convert to percentage
    percent = (currentTime - startTime) / (TOTAL_SEC * 1000) * 100;

    $("#progressbar").progressbar({ value: percent });

    if (percent >= 100) {
        window.location = "newLocation.html";
    } else {
        setTimeout(updateProgress, 1000 / FRAMES_PER_SEC);
    }                 
}

이것은 제가이 일을하는 제 음악 프로젝트를 위해 만든 타이머입니다. 모든 장치에서 정확한 타이머.

var Timer = function(){
  var framebuffer = 0,
  var msSinceInitialized = 0,
  var timer = this;

  var timeAtLastInterval = new Date().getTime();

  setInterval(function(){
    var frametime = new Date().getTime();
    var timeElapsed = frametime - timeAtLastInterval;
    msSinceInitialized += timeElapsed;
    timeAtLastInterval = frametime;
  },1);

  this.setInterval = function(callback,timeout,arguments) {
    var timeStarted = msSinceInitialized;
    var interval = setInterval(function(){
      var totaltimepassed = msSinceInitialized - timeStarted;
      if (totaltimepassed >= timeout) {
        callback(arguments);
        timeStarted = msSinceInitialized;
      }
    },1);

    return interval;
  }
}

var timer = new Timer();
timer.setInterval(function(){console.log("This timer will not drift."),1000}


말하기는 싫지만이를 완화 할 방법이 없다고 생각합니다. 나는 그것이 클라이언트 시스템에 달려 있다고 생각하므로 더 빠른 자바 스크립트 엔진이나 기계가 약간 더 정확할 수 있습니다.


내 경험에 따르면 js가 작동하는 가장 작은 합리적인 시간이 약 32-33ms이지만 노력을 잃었습니다. ...


There is definitely a limitation here. To give you some perspective, the Chrome browser Google just released is fast enough that it can execute setTimeout(function() {}, 0) in 15-20 ms whereas older Javascript engines took hundreds of milliseconds to execute that function. Although setTimeout uses milliseconds, no javascript virtual machine at this point in time can execute code with that precision.


Dan, from my experience (that includes implementation of SMIL2.1 language in JavaScript, where time management is in subject) I can assure you that you actually never need high precision of setTimeout or setInterval.

What does however matter is the order in which setTimeout/setInterval gets executed when queued - and that always works perfectly.


JavaScript timeouts have a defacto limit of 10-15ms (I'm not sure what you're doing to get 200ms, unless you're doing 185ms of actual js execution). This is due to windows having a standard timer resolution of 15ms, the only way to do better is to use Windows' higher resolution timers which is a system wide setting so can screw with other applications on the system and also chews battery life (Chrome has a bug from Intel on this issue).

The defacto standard of 10-15ms is due to people using 0ms timeouts on websites but then coding in a way that assumes that assumes a 10-15ms timeout (eg. js games which assume 60fps but ask 0ms/frame with no delta logic so the game/site/animation goes a few orders of magnitude faster than intended). To account for that, even on platforms that don't have windows' timer problems, the browsers limit timer resolution to 10ms.


Here are what I use. Since it's JavaScript, I will post both my Frontend and node.js solutions:

For both, I use the same decimal rounding function that I highly recommend you keep at arms length because reasons:

const round = (places, number) => +(Math.round(number + `e+${places}`) + `e-${places}`)

places - Number of decimal places at which to round, this should be safe and should avoid any issues with floats (some numbers like 1.0000000000005~ can be problematic). I Spent time researching the best way to round decimals provided by high-resolution timers converted to milliseconds.

that + symbol - It is a unary operator that converts an operand into a number, virtually identical to Number()

Browser

const start = performance.now()

// I wonder how long this comment takes to parse

const end = performance.now()

const result = (end - start) + ' ms'

const adjusted = round(2, result) // see above rounding function

node.js

// Start timer
const startTimer = () => process.hrtime()

// End timer
const endTimer = (time) => {
    const diff = process.hrtime(time)
    const NS_PER_SEC = 1e9
    const result = (diff[0] * NS_PER_SEC + diff[1])
    const elapsed = Math.round((result * 0.0000010))
    return elapsed
}

// This end timer converts the number from nanoseconds into milliseconds;
// you can find the nanosecond version if you need some seriously high-resolution timers.

const start = startTimer()

// I wonder how long this comment takes to parse

const end = endTimer(start)

console.log(end + ' ms')

You could consider using the html5 webaudio clock which uses the system time for better accuracy

참고URL : https://stackoverflow.com/questions/196027/is-there-a-more-accurate-way-to-create-a-javascript-timer-than-settimeout

반응형