setTimeout ()에 남은 시간을 찾으십니까?


91

내가 소유하지 않은 라이브러리 코드와 상호 작용하고 (합리적으로) 변경할 수없는 Javascript를 작성하고 있습니다. 일련의 시간 제한 질문에서 다음 질문을 표시하는 데 사용되는 Javascript 시간 제한을 생성합니다. 이것은 모든 희망을 넘어서 난독 화되어 있기 때문에 실제 코드가 아닙니다. 라이브러리가 수행하는 작업은 다음과 같습니다.

....
// setup a timeout to go to the next question based on user-supplied time
var t = questionTime * 1000
test.currentTimeout = setTimeout( showNextQuestion(questions[i+1]), t );

questionTime * 1000의해 생성 된 타이머를 조사하여 채워지는 진행률 표시 줄을 화면에 표시하고 싶습니다 setTimeout. 유일한 문제는이를 수행 할 방법이없는 것 같습니다. 거기에 getTimeout내가 부족이 함수는? 내가 찾을 수있는 유일한 Javascript 시간 초과 정보는을 통한 생성 setTimeout( function, time)및 삭제와 관련이 있습니다 clearTimeout( id ).

시간 초과가 발생하기 전에 남은 시간 또는 시간 초과가 호출 된 후 경과 된 시간을 반환하는 함수를 찾고 있습니다. 내 진행 바코드는 다음과 같습니다.

var  timeleft = getTimeout( test.currentTimeout ); // I don't know how to do this
var  $bar = $('.control .bar');
while ( timeleft > 1 ) {
    $bar.width(timeleft / test.defaultQuestionTime * 1000);
}

tl; dr : javascript setTimeout () 전에 남은 시간을 어떻게 찾습니까?


지금 사용하고있는 솔루션은 다음과 같습니다. 나는 테스트를 담당하는 라이브러리 섹션을 살펴보고 코드를 풀었다 (끔찍하고 내 권한에 반함).

// setup a timeout to go to the next question based on user-supplied time
var t = questionTime * 1000
test.currentTimeout = mySetTimeout( showNextQuestion(questions[i+1]), t );

내 코드는 다음과 같습니다.

// setTimeout 래퍼
function mySetTimeout (func, timeout) {
    타임 아웃 [n = setTimeout (func, timeout)] = {
        시작 : new Date (). getTime (),
        끝 : new Date (). getTime () + timeout
        t : 시간 초과
    }
    반환 n;
}

이것은 IE 6이 아닌 모든 브라우저에서 잘 작동합니다. 심지어 원래의 iPhone에서도 비 동기화 될 것으로 예상했습니다.

답변:


31

라이브러리 코드를 수정할 수없는 경우 목적에 맞게 setTimeout을 다시 정의해야합니다. 다음은 수행 할 수있는 작업의 예입니다.

(function () {
var nativeSetTimeout = window.setTimeout;

window.bindTimeout = function (listener, interval) {
    function setTimeout(code, delay) {
        var elapsed = 0,
            h;

        h = window.setInterval(function () {
                elapsed += interval;
                if (elapsed < delay) {
                    listener(delay - elapsed);
                } else {
                    window.clearInterval(h);
                }
            }, interval);
        return nativeSetTimeout(code, delay);
    }

    window.setTimeout = setTimeout;
    setTimeout._native = nativeSetTimeout;
};
}());
window.bindTimeout(function (t) {console.log(t + "ms remaining");}, 100);
window.setTimeout(function () {console.log("All done.");}, 1000);

이것은 프로덕션 코드는 아니지만 올바른 방향으로 나아가 야합니다. 제한 시간당 하나의 리스너 만 바인드 할 수 있습니다. 나는 이것으로 광범위한 테스트를 수행하지 않았지만 Firebug에서 작동합니다.

보다 강력한 솔루션은 setTimeout을 래핑하는 동일한 기술을 사용하지만 대신 반환 된 timeoutId에서 리스너로의 맵을 사용하여 시간 초과 당 여러 리스너를 처리합니다. 시간 제한이 지워진 경우 리스너를 분리 할 수 ​​있도록 clearTimeout 래핑을 고려할 수도 있습니다.


감사합니다.
xecutioner

14
실행 시간으로 인해 잠시 동안 몇 밀리 초 정도 드리프트 될 수 있습니다. 더 안전한 방법은 타임 아웃을 시작한 시간 (new Date ())을 기록한 다음 현재 시간에서 빼는 것입니다 (또 다른 new Date ())
Jonathan

61

기록을 위해 node.js에 남은 시간을 얻는 방법이 있습니다.

var timeout = setTimeout(function() {}, 3600 * 1000);

setInterval(function() {
    console.log('Time left: '+getTimeLeft(timeout)+'s');
}, 2000);

function getTimeLeft(timeout) {
    return Math.ceil((timeout._idleStart + timeout._idleTimeout - Date.now()) / 1000);
}

인쇄물:

$ node test.js 
Time left: 3599s
Time left: 3597s
Time left: 3595s
Time left: 3593s

이것은 파이어 폭스에서 작동하지 않는 것 같지만 node.js는 자바 스크립트이기 때문에 노드 솔루션을 찾는 사람들에게 도움이 될 것이라고 생각했습니다.


5
노드의 새 버전인지 또는 무엇인지 확실하지 않지만 이것이 작동하려면 "getTime ()"부분을 제거해야했습니다. _idleStart 정수 지금 이미 보인다
kasztelan

3
방금 노드 0.10에서 시도했지만 getTime()제거되었습니다. _idleStart이미 시간입니다
Tan Nguyen

2
제한 시간 구조가 해당 정보의 누출이기 때문에 노드 6.3에서는 작동하지 않습니다.
Huan의

53

편집 : 실제로 더 나은 것을 만들었다 고 생각합니다. https://stackoverflow.com/a/36389263/2378102

이 함수를 작성했고 많이 사용합니다.

function timer(callback, delay) {
    var id, started, remaining = delay, running

    this.start = function() {
        running = true
        started = new Date()
        id = setTimeout(callback, remaining)
    }

    this.pause = function() {
        running = false
        clearTimeout(id)
        remaining -= new Date() - started
    }

    this.getTimeLeft = function() {
        if (running) {
            this.pause()
            this.start()
        }

        return remaining
    }

    this.getStateRunning = function() {
        return running
    }

    this.start()
}

타이머 만들기 :

a = new timer(function() {
    // What ever
}, 3000)

따라서 남은 시간을 원한다면 다음을 수행하십시오.

a.getTimeLeft()

감사합니다. 아니 정확히 내가 찾고 있지만, 남은 시간을 계산하는 기능을 봤는데 무슨 일이 매우 유용합니다
대신

4

Javascript의 이벤트 스택은 생각하는대로 작동하지 않습니다.

시간 초과 이벤트가 생성되면 이벤트 큐에 추가되지만 해당 이벤트가 발생하는 동안 다른 이벤트가 우선 순위를 갖게되어 실행 시간이 지연되고 런타임이 연기 될 수 있습니다.

예 : 화면에 무언가를 알리기 위해 10 초 지연 시간 제한을 만듭니다. 이벤트 스택에 추가되고 모든 현재 이벤트가 발생한 후에 실행됩니다 (일부 지연 발생). 그런 다음 시간 초과가 처리 될 때 브라우저는 계속해서 다른 이벤트를 캡처하여 스택에 추가하므로 처리가 더 지연됩니다. 사용자가 클릭하거나 Ctrl + 입력을 많이하면 해당 이벤트가 현재 스택보다 우선합니다. 10 초는 15 초 이상으로 바뀔 수 있습니다.


즉, 얼마나 많은 시간이 지 났는지 속이는 방법은 여러 가지가 있습니다. 한 가지 방법은 스택에 setTimeout을 추가 한 직후에 setInterval을 실행하는 것입니다.

예 : 10 초 지연으로 settimeout을 수행합니다 (해당 지연을 글로벌에 저장). 그런 다음 초당 실행되는 setInterval을 수행하여 지연에서 1을 빼고 남은 지연을 출력합니다. 이벤트 스택이 실제 시간에 영향을 미칠 수있는 방식 (위에서 설명) 때문에 이것은 여전히 ​​정확하지는 않지만 카운트를 제공합니다.


요컨대, 남은 시간을 얻는 실제 방법은 없습니다. 사용자에게 견적을 전달하는 방법은 유일한 방법입니다.


4

서버 측 Node.js 특정

위의 어느 것도 나를 위해 실제로 효과가 없었으며 시간 초과 개체를 검사 한 후 모든 것이 프로세스가 시작된 시점과 관련된 것처럼 보였습니다. 다음은 나를 위해 일했습니다.

myTimer = setTimeout(function a(){console.log('Timer executed')},15000);

function getTimeLeft(timeout){
  console.log(Math.ceil((timeout._idleStart + timeout._idleTimeout)/1000 - process.uptime()));
}

setInterval(getTimeLeft,1000,myTimer);

산출:

14
...
3
2
1
Timer executed
-0
-1
...

node -v
v9.11.1

간결성을 위해 편집 된 출력이지만이 기본 기능은 실행 또는 실행 이후의 대략적인 시간을 제공합니다. 다른 사람들이 언급했듯이 노드가 처리하는 방식 때문에 정확하지는 않지만 1 분 전에 실행 된 요청을 억제하고 타이머를 저장 한 경우 이것이 왜 그렇지 않은지 알 수 없습니다. 빠른 확인으로 작동합니다. 10.2 이상에서 refreshtimer로 개체를 저글링하는 것이 흥미로울 수 있습니다.


1

당신은 할 수 수정 setTimeout 맵에서 각 타임 아웃의 종료 시간을 저장하고라는 함수 만들기 위해 getTimeout특정 ID로 타임 아웃을 위해 남은 시간을 얻을 수 있습니다.

이것은 슈퍼솔루션 이었지만 약간 더 적은 메모리를 사용하도록 수정했습니다.

let getTimeout = (() => { // IIFE
    let _setTimeout = setTimeout, // Reference to the original setTimeout
        map = {}; // Map of all timeouts with their end times

    setTimeout = (callback, delay) => { // Modify setTimeout
        let id = _setTimeout(callback, delay); // Run the original, and store the id
        map[id] = Date.now() + delay; // Store the end time
        return id; // Return the id
    };

    return (id) => { // The actual getTimeout function
        // If there was no timeout with that id, return NaN, otherwise, return the time left clamped to 0
        return map[id] ? Math.max(map[id] - Date.now(), 0) : NaN;
    }
})();

용법:

// go home in 4 seconds
let redirectTimeout = setTimeout(() => {
    window.location.href = "/index.html";
}, 4000);

// display the time left until the redirect
setInterval(() => {
    document.querySelector("#countdown").innerHTML = `Time left until redirect ${getTimeout(redirectTimeout)}`;
},1);

getTimeout IIFE 의 축소 된 버전은 다음과 같습니다 .

let getTimeout=(()=>{let t=setTimeout,e={};return setTimeout=((a,o)=>{let u=t(a,o);return e[u]=Date.now()+o,u}),t=>e[t]?Math.max(e[t]-Date.now(),0):NaN})();

이것이 나만큼이나 당신에게 유용하기를 바랍니다! :)


0

아니요,하지만 함수에서 애니메이션에 대한 자체 setTimeout / setInterval을 가질 수 있습니다.

질문이 다음과 같다고 말합니다.

function myQuestion() {
  // animate the progress bar for 1 sec
  animate( "progressbar", 1000 );

  // do the question stuff
  // ...
}

그리고 애니메이션은 다음 두 가지 기능으로 처리됩니다.

function interpolate( start, end, pos ) {
  return start + ( pos * (end - start) );
}

function animate( dom, interval, delay ) {

      interval = interval || 1000;
      delay    = delay    || 10;

  var start    = Number(new Date());

  if ( typeof dom === "string" ) {
    dom = document.getElementById( dom );
  }

  function step() {

    var now     = Number(new Date()),
        elapsed = now - start,
        pos     = elapsed / interval,
        value   = ~~interpolate( 0, 500, pos ); // 0-500px (progress bar)

    dom.style.width = value + "px";

    if ( elapsed < interval )
      setTimeout( step, delay );
  }

  setTimeout( step, delay );
}

애니메이션이 함수의 맨 위에서 시작되기 때문에 차이는 ms 단위로만 측정 할 수 있습니다. 나는 당신이 jQuery를 사용하는 것을 보았다. 그러면 jquery.animate를 사용할 수 있습니다.
gblazex 2010-06-29

가장 좋은 방법은 직접 시도해 보는 것입니다. :)
gblazex 2010-06-29

0

누군가 이것을 되돌아보고 있다면. 타임 아웃이나 인터벌에 남은 시간을 알려줄뿐만 아니라 다른 작업도 수행 할 수있는 타임 아웃 및 인터벌 관리자가 나왔습니다. 더 멋지고 더 정확하게 만들기 위해 추가 할 것이지만, 꽤 잘 작동하는 것 같습니다 (더 정확할 수있는 아이디어가 더 있지만).

https://github.com/vhmth/Tock


귀하의 링크가 깨진
킥 Buttowski

0

질문은 이미 답변되었지만 내 비트를 추가하겠습니다. 그것은 나에게 일어났다.

사용 setTimeoutrecursion다음과 같이 :

var count = -1;

function beginTimer()
{
    console.log("Counting 20 seconds");
    count++;

    if(count <20)
    {
        console.log(20-count+"seconds left");
        setTimeout(beginTimer,2000);
    }
    else
    {
        endTimer();
    }
}

function endTimer()
{
    console.log("Time is finished");
}

코드가 자명하다고 생각합니다.


0

이것을 확인하십시오 :

class Timer {
  constructor(fun,delay) {
    this.timer=setTimeout(fun, delay)
    this.stamp=new Date()
  }
  get(){return ((this.timer._idleTimeout - (new Date-this.stamp))/1000) }
  clear(){return (this.stamp=null, clearTimeout(this.timer))}
}

타이머 만들기 :

let smtg = new Timer(()=>{do()}, 3000})

남아 있으십시오 :

smth.get()

시간 초과 지우기

smth.clear()

0
    (function(){
        window.activeCountdowns = [];
        window.setCountdown = function (code, delay, callback, interval) {
            var timeout = delay;
            var timeoutId = setTimeout(function(){
                clearCountdown(timeoutId);
                return code();
            }, delay);
            window.activeCountdowns.push(timeoutId);
            setTimeout(function countdown(){
                var key = window.activeCountdowns.indexOf(timeoutId);
                if (key < 0) return;
                timeout -= interval;
                setTimeout(countdown, interval);
                return callback(timeout);
            }, interval);
            return timeoutId;
        };
        window.clearCountdown = function (timeoutId) {
            clearTimeout(timeoutId);
            var key = window.activeCountdowns.indexOf(timeoutId);
            if (key < 0) return;
            window.activeCountdowns.splice(key, 1);
        };
    })();

    //example
    var t = setCountdown(function () {
        console.log('done');
    }, 15000, function (i) {
        console.log(i / 1000);
    }, 1000);

0

나는이 답을 찾기 위해 여기에 들렀지만 내 문제를 지나치게 생각했습니다. setTimeout이 진행되는 동안 시간을 ​​추적하기 만하면되므로 여기에있는 경우 다른 방법이 있습니다.

    var focusTime = parseInt(msg.time) * 1000

    setTimeout(function() {
        alert('Nice Job Heres 5 Schrute bucks')
        clearInterval(timerInterval)
    }, focusTime)

    var timerInterval = setInterval(function(){
        focusTime -= 1000
        initTimer(focusTime / 1000)
    }, 1000);

0

더 빠르고 쉬운 방법 :

tmo = 1000;
start = performance.now();
setTimeout(function(){
    foo();
},tmo);

남은 시간은 다음을 통해 얻을 수 있습니다.

 timeLeft = tmo - (performance.now() - start);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.