Javascript 在 setTimeout() 中找到剩余的时间?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3144711/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
find the time left in a setTimeout()?
提问by Just Jake
I'm writing some Javascript that interacts with library code that I don't own, and can't (reasonably) change. It creates Javascript timeouts used for showing the next question in a series of time-limited questions. This isn't real code because it is obfuscated beyond all hope. Here's what the library is doing:
我正在编写一些与我不拥有的库代码交互的 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 );
I want to put a progress bar onscreen that fills towards questionTime * 1000by interrogating the timer created by setTimeout. The only problem is, there seems to be no way to do this. Is there a getTimeoutfunction that I'm missing? The only information on Javascript timeouts that I can find is related only to creation via setTimeout( function, time)and deletion via clearTimeout( id ).
我想在屏幕上放置一个进度条,questionTime * 1000通过询问由setTimeout. 唯一的问题是,似乎没有办法做到这一点。是否有getTimeout我缺少的功能?我能找到的有关 Javascript 超时的唯一信息仅setTimeout( function, time)与通过clearTimeout( id ).
I'm looking for a function that returns either the time remaining before a timeout fires, or the time elapsed after a timeout has been called. My progress bar code looks like this:
我正在寻找一个函数,该函数返回超时触发前的剩余时间,或调用超时后经过的时间。我的进度条代码如下所示:
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: How do I find the time remaining before a javascript setTimeout()?
tl; dr:我如何找到 javascript setTimeout() 之前的剩余时间?
Here's the solution I'm using now. I went through the library section that's in charge of tests, and unscrambled the code (terrible, and against my permissions).
这是我现在使用的解决方案。我浏览了负责测试的库部分,并解读了代码(太糟糕了,而且违反了我的权限)。
// 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 );
and here's my code:
这是我的代码:
// wrapper for setTimeout
function mySetTimeout( func, timeout ) {
timeouts[ n = setTimeout( func, timeout ) ] = {
start: new Date().getTime(),
end: new Date().getTime() + timeout
t: timeout
}
return n;
}
This works pretty spot-on in any browser that isn't IE 6. Even the original iPhone, where I expected things to get asynchronous.
这在不是 IE 6 的任何浏览器中都非常有效。即使是最初的 iPhone,我希望事情会变得异步。
采纳答案by lawnsea
If you can't modify the library code, you'll need to redefine setTimeout to suit your purposes. Here's an example of what you could do:
如果您无法修改库代码,则需要重新定义 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);
This is not production code, but it should put you on the right track. Note that you can only bind one listener per timeout. I haven't done extensive testing with this, but it works in Firebug.
这不是生产代码,但它应该让您走上正轨。请注意,每次超时只能绑定一个侦听器。我没有对此进行广泛的测试,但它在 Firebug 中有效。
A more robust solution would use the same technique of wrapping setTimeout, but instead use a map from the returned timeoutId to listeners to handle multiple listeners per timeout. You might also consider wrapping clearTimeout so you can detach your listener if the timeout is cleared.
更健壮的解决方案将使用包装 setTimeout 的相同技术,而是使用从返回的 timeoutId 到侦听器的映射来处理每个超时的多个侦听器。您也可以考虑包装 clearTimeout 以便在超时被清除时分离您的侦听器。
回答by Fluffy
Just for the record, there is a way to get the time left in node.js:
只是为了记录,有一种方法可以在 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);
}
Prints:
印刷:
$ node test.js
Time left: 3599s
Time left: 3597s
Time left: 3595s
Time left: 3593s
This doesn't seem to work in firefox through, but since node.js is javascript, I thought this remark might be helpful for people looking for the node solution.
这在 firefox 中似乎不起作用,但由于 node.js 是 javascript,我认为这句话可能对寻找 node 解决方案的人有所帮助。
回答by super
EDIT: I actually think I made an even better one: https://stackoverflow.com/a/36389263/2378102
编辑:我实际上认为我做了一个更好的:https: //stackoverflow.com/a/36389263/2378102
I wrote this function and I use it a lot:
我写了这个函数并且我经常使用它:
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()
}
Make a timer:
做一个计时器:
a = new timer(function() {
// What ever
}, 3000)
So if you want the time remaining just do:
因此,如果您想要剩余的时间,请执行以下操作:
a.getTimeLeft()
回答by vol7ron
Javascript's event stacks don't operate how you would think.
Javascript 的事件堆栈不会按照您的想法运行。
When a timeout event is created, it is added to the event queue, but other events may take priority while that event is being fired, delay the execution time and postponing runtime.
创建超时事件后,它会被添加到事件队列中,但在触发该事件时其他事件可能优先,延迟执行时间并推迟运行时间。
Example:You create a timeout with a delay of 10 seconds to alert something to the screen. It will be added to the event stack and will be executed after all current events are fired (causing some delay). Then, when the timeout is processed, the browser still continues to capture other events add them to the stack, which causes further delays in the processing. If the user clicks, or does a lot of ctrl+typing, their events take priority over the current stack. Your 10 seconds can turn into 15 seconds, or longer.
示例:您创建了一个延迟 10 秒的超时来向屏幕发出警报。它将被添加到事件堆栈中,并将在所有当前事件被触发后执行(导致一些延迟)。然后,当超时被处理时,浏览器仍然继续捕获其他事件将它们添加到堆栈中,这导致处理中的进一步延迟。如果用户单击或执行大量 ctrl+typing,则他们的事件优先于当前堆栈。您的 10 秒可以变成 15 秒或更长。
That being said, there are many ways to fake how much time has passed. One way is to execute a setInterval right after you add the setTimeout to the stack.
话虽如此,有很多方法可以伪造已经过去了多少时间。一种方法是在将 setTimeout 添加到堆栈后立即执行 setInterval。
Example:Perform a settimeout with a 10 second delay (store that delay in a global). Then perform a setInterval that runs every second to subtract 1 from the delay and output the delay remaining. Because of how the event stack can influence actual time (described above), this still won't be accurate, but does give a count.
示例:执行具有 10 秒延迟的设置超时(将该延迟存储在全局中)。然后执行每秒运行一次的 setInterval 以从延迟中减去 1 并输出剩余的延迟。由于事件堆栈如何影响实际时间(如上所述),这仍然不准确,但确实提供了一个计数。
In short, there is no real way to get the remaining time. There are only ways to try and convey an estimate to the user.
简而言之,没有真正的方法来获得剩余时间。只有几种方法可以尝试向用户传达估算值。
回答by super
Here is maybe an even better way to do it, plus, you won't need to change code you've already written:
这可能是一种更好的方法,此外,您无需更改已经编写的代码:
var getTimeout = (function() { // IIFE
var _setTimeout = setTimeout, // Reference to the original setTimeout
map = {}; // Map of all timeouts with their start date and delay
setTimeout = function(callback, delay) { // Modify setTimeout
var id = _setTimeout(callback, delay); // Run the original, and store the id
map[id] = [Date.now(), delay]; // Store the start date and delay
return id; // Return the id
};
return function(id) { // The actual getTimeLeft function
var m = map[id]; // Find the timeout in map
// If there was no timeout with that id, return NaN, otherwise, return the time left clamped to 0
return m ? Math.max(m[1] - Date.now() + m[0], 0) : NaN;
}
})();
... And mimimized:
...并最小化:
var getTimeout=function(){var e=setTimeout,b={};setTimeout=function(a,c){var d=e(a,c);b[d]=[Date.now(),c];return d};return function(a){return(a=b[a])?Math.max(a[1]-Date.now()+a[0],0):NaN}}();
回答by John Watts
Server side Node.js specific
服务器端 Node.js 特定
None of the above really worked for me, and after inspecting the timeout object it looked like everything was relative to when the process started. The following worked for me:
以上都不对我有用,在检查超时对象后,看起来一切都与进程开始时有关。以下对我有用:
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);
Output:
输出:
14
...
3
2
1
Timer executed
-0
-1
...
node -v
v9.11.1
Edited output for brevity, but this basic function gives a approximate time until execution or since execution. As others mention, none of this will be exact due to the way node processes, but if I want to suppress a request that was run less than 1 minute ago, and I stored the timer, I don't see why this wouldn't work as a quick check. Could be interesting to juggle objects with refreshtimer in 10.2+.
为简洁起见编辑了输出,但此基本函数给出了执行前或执行后的大致时间。正如其他人提到的,由于节点处理的方式,这一切都不是准确的,但是如果我想抑制不到 1 分钟前运行的请求,并且我存储了计时器,我不明白为什么这不会作为快速检查工作。在 10.2+ 中使用 refreshtimer 处理对象可能很有趣。
回答by Jbur43
I stopped by here looking for this answer, but was overthinking my problem. If you are here because you just need to keep track of time while you're setTimeout is in progress, here's another way to do it:
我在这里停下来寻找这个答案,但过度思考了我的问题。如果你在这里是因为你只需要在 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);
回答by Kimeiga
You can modifysetTimeoutto store each timeout's end time in a map and create a function called getTimeoutto get the time left for a timeout with a certain id.
您可以修改setTimeout以将每个超时的结束时间存储在地图中,并创建一个调用的函数getTimeout以获取具有特定 id 的超时剩余时间。
This was super's solution, but I modified it to use slightly less memory
这是super的解决方案,但我对其进行了修改以使用更少的内存
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;
}
})();
Usage:
用法:
// 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);
Here's a minified version of this getTimeoutIIFE:
这是这个getTimeoutIIFE的缩小版本:
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})();
I hope this is as useful to you as it was for me! :)
我希望这对你和我一样有用!:)
回答by Vinay
If anyone's looking back on this. I've come out with a timeout and interval manager that can get you the time left in a timeout or interval as well as do some other stuff. I'll be adding to it to make it more nifty and more accurate, but it seems to work fairly well as is (although I have some more ideas to make it even more accurate):
如果有人回顾这一点。我已经推出了一个超时和间隔管理器,它可以让你在超时或间隔中剩余的时间以及做一些其他的事情。我将添加它以使其更漂亮和更准确,但它似乎工作得相当好(尽管我有更多想法可以使它更准确):
回答by dasfdsa
Question has already been answered but I will add my bit. It just occured to me.
问题已经得到回答,但我会补充一点。它只是发生在我身上。
Use setTimeoutin recursionas follows:
使用setTimeout中recursion,如下所示:
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");
}
I guess the code is self explanatory
我想代码是不言自明的

