Piszę trochę Javascript, który współdziała z kodem biblioteki, którego nie jestem właścicielem i nie mogę (rozsądnie) zmienić. Tworzy limity czasu JavaScript używane do wyświetlania następnego pytania w serii pytań ograniczonych w czasie. To nie jest prawdziwy kod, ponieważ jest zaciemniony poza wszelką nadzieją. Oto, co robi biblioteka:
....
// 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 );
Chcę umieścić na ekranie pasek postępu, który zapełnia się, questionTime * 1000
sprawdzając licznik czasu utworzony przez setTimeout
. Jedynym problemem jest to, że wydaje się, że nie da się tego zrobić. Czy jest getTimeout
funkcja, której mi brakuje? Jedyne informacje na temat limitów czasu Javascript, które mogę znaleźć, dotyczą tylko tworzenia setTimeout( function, time)
i usuwania przez clearTimeout( id )
.
Szukam funkcji, która zwraca czas pozostały do uruchomienia limitu czasu lub czas, który upłynął po wywołaniu limitu czasu. Mój kod paskowy postępu wygląda następująco:
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: Jak znaleźć czas pozostały do wykonania setTimeout () w javascript?
Oto rozwiązanie, którego teraz używam. Przejrzałem sekcję biblioteki, która jest odpowiedzialna za testy, i rozszyfrowałem kod (okropne i wbrew moim uprawnieniom).
// 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 );
a oto mój kod:
// opakowanie dla setTimeout function mySetTimeout (func, timeout) { timeouts [n = setTimeout (func, timeout)] = { start: new Date (). getTime (), end: new Date (). getTime () + timeout t: limit czasu } return n; }
Działa to bardzo dobrze w każdej przeglądarce innej niż IE 6. Nawet w oryginalnym iPhonie, w którym spodziewałem się, że wszystko będzie asynchroniczne.
źródło
Tak dla przypomnienia, istnieje sposób na pobranie czasu pozostałego w 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); }
Wydruki:
$ node test.js Time left: 3599s Time left: 3597s Time left: 3595s Time left: 3593s
Wydaje się, że to nie działa w przeglądarce Firefox, ale ponieważ node.js jest javascript, pomyślałem, że ta uwaga może być pomocna dla osób szukających rozwiązania dla węzłów.
źródło
getTime()
jest usunięty,_idleStart
jest już czasEDYCJA: Właściwie myślę, że zrobiłem jeszcze lepszy: https://stackoverflow.com/a/36389263/2378102
Napisałem tę funkcję i często jej używam:
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() }
Zrób minutnik:
a = new timer(function() { // What ever }, 3000)
Więc jeśli chcesz, aby pozostał czas, po prostu zrób:
źródło
Stosy zdarzeń JavaScript nie działają tak, jak myślisz.
Po utworzeniu zdarzenia przekroczenia limitu czasu jest ono dodawane do kolejki zdarzeń, ale inne zdarzenia mogą mieć priorytet podczas wyzwalania tego zdarzenia, opóźniać czas wykonania i opóźniać czas wykonywania.
Przykład: Tworzysz limit czasu z opóźnieniem 10 sekund, aby zaalarmować coś na ekranie. Zostanie dodany do stosu zdarzeń i zostanie wykonany po uruchomieniu wszystkich bieżących zdarzeń (powodując pewne opóźnienie). Następnie po przetworzeniu limitu czasu przeglądarka nadal przechwytuje inne zdarzenia, dodaje je do stosu, co powoduje dalsze opóźnienia w przetwarzaniu. Jeśli użytkownik kliknie lub często wciska klawisze Ctrl + klawisz, jego zdarzenia mają pierwszeństwo przed bieżącym stosem. Twoje 10 sekund może zmienić się w 15 sekund lub dłużej.
Biorąc to pod uwagę, istnieje wiele sposobów, aby udawać, ile czasu minęło. Jednym ze sposobów jest wykonanie setInterval zaraz po dodaniu setTimeout do stosu.
Przykład: Ustaw limit czasu z 10-sekundowym opóźnieniem (zapisz to opóźnienie w globalnej). Następnie wykonaj setInterval, który jest uruchamiany co sekundę, aby odjąć 1 od opóźnienia i wyprowadzić pozostałe opóźnienie. Ze względu na to, jak stos zdarzeń może wpływać na rzeczywisty czas (opisany powyżej), nadal nie będzie to dokładne, ale daje liczbę.
Krótko mówiąc, nie ma prawdziwego sposobu na uzyskanie pozostałego czasu. Są tylko sposoby, aby spróbować przekazać użytkownikowi oszacowanie.
źródło
Specyficzne dla Node.js po stronie serwera
Żadne z powyższych nie działało dla mnie, a po sprawdzeniu obiektu limitu czasu wyglądało na to, że wszystko było zależne od momentu rozpoczęcia procesu. Pracowały dla mnie:
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);
Wynik:
14 ... 3 2 1 Timer executed -0 -1 ... node -v v9.11.1
Edytowano dane wyjściowe dla zwięzłości, ale ta podstawowa funkcja podaje przybliżony czas do wykonania lub od wykonania. Jak wspominają inni, nic z tego nie będzie dokładne ze względu na sposób, w jaki węzeł przetwarza, ale jeśli chcę zablokować żądanie, które zostało uruchomione mniej niż 1 minutę temu, i zapisałem licznik czasu, nie rozumiem, dlaczego nie działa jako szybki test. Żonglowanie obiektami za pomocą odświeżacza w wersji 10.2+ może być interesujące.
źródło
Możesz zmodyfikować,
setTimeout
aby zapisać czas zakończenia każdego limitu czasu na mapie i utworzyć funkcję wywoływaną wgetTimeout
celu uzyskania czasu pozostałego do limitu czasu z określonym identyfikatorem.To był wspaniały jest rozwiązanie , ale zmodyfikowano go użyć nieco mniej pamięci
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; } })();
Stosowanie:
// 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);
Oto
getTimeout
zminimalizowana wersja tego 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})();
Mam nadzieję, że jest to dla Ciebie tak samo przydatne, jak dla mnie! :)
źródło
Nie, ale możesz mieć swój własny setTimeout / setInterval dla animacji w swojej funkcji.
Powiedz, że Twoje pytanie wygląda następująco:
function myQuestion() { // animate the progress bar for 1 sec animate( "progressbar", 1000 ); // do the question stuff // ... }
Twoja animacja będzie obsługiwana przez te 2 funkcje:
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 ); }
źródło
Jeśli ktoś patrzy wstecz. Wyszedłem z menedżera limitu czasu i interwałów, który może uzyskać czas pozostały w limicie czasu lub interwale, a także zrobić kilka innych rzeczy. Dodam do tego, aby był ładniejszy i dokładniejszy, ale wydaje się, że działa całkiem nieźle, jak jest (chociaż mam więcej pomysłów, aby był jeszcze dokładniejszy):
https://github.com/vhmth/Tock
źródło
Odpowiedź na pytanie już została udzielona, ale dodam swoją część. Po prostu przyszło mi to do głowy.
Użyj
setTimeout
wrecursion
następujący sposób: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"); }
Myślę, że kod jest oczywisty
źródło
Sprawdź ten:
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))} }
Zrób minutnik:
let smtg = new Timer(()=>{do()}, 3000})
Pozostań:
Wyczyść limit czasu
źródło
(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);
źródło
Zatrzymałem się tutaj, szukając odpowiedzi, ale zastanawiałem się nad swoim problemem. Jeśli jesteś tutaj, ponieważ musisz po prostu śledzić czas podczas ustawiania limitu czasu, oto inny sposób, aby to zrobić:
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);
źródło
Szybszy i łatwiejszy sposób:
tmo = 1000; start = performance.now(); setTimeout(function(){ foo(); },tmo);
Pozostały czas możesz uzyskać dzięki:
źródło