I have a timer, I want to do something when textContent of div element === 0;
JavaScript:
function createTimer() {
const display = document.createElement('div');
display.classList.add('display');
display.id = 'display';
display.textContent = '5';
return display;
};
let intervalID;
function startTimer() {
resetTimer();
intervalID = setInterval(() => {
let displayTimer = document.getElementById('display');
let displayNumber = parseInt(displayTimer.textContent);
if (displayTimer.textContent !== '0') displayTimer.textContent = displayNumber - 1;
}, 1000);
};
function resetTimer() {
clearInterval(intervalID);
};
function someFunc() {
// here is a lot of code stuff and timer is working correctly
const timer = createTimer();
};
This is what i tried:
function someFunc() {
const timer = createTimer();
timer.addEventListener('input', () => {
if (timer.textContent === '0') {
console.log(true);
};
});
};
As far as I understood correctly, by creating input event on timer, I always get timer.textContent when it changes, right? I keep track of all the changes that's happening in this div element.
Nothing happens.
Keep track of your count as a number in JavaScript. This creates clarity in what the count is and how it can be manipulated.
Inside the setInterval callback, check if the count is 0. The interval is already there and will run every second, so it makes sense to check it in that place.
The example below is a modified version of your script with the suggested implementation. Since you're returning the display element from the createTimer function I've decided to reuse it in the startTimer function. That way you don't have to select the element from the DOM, as you already have a reference to the element.
As a bonus an extra argument which can be callback function to do something whenever the timer ends.
let count = 5;
let intervalID;
function createTimer() {
const display = document.createElement('div');
display.classList.add('display');
display.id = 'display';
display.textContent = '5';
return display;
};
function resetTimer() {
clearInterval(intervalID);
};
function startTimer(timer, onFinish) {
resetTimer();
intervalID = setInterval(() => {
if (count !== 0) {
count--;
}
if (count === 0) {
resetTimer();
if (typeof onFinish === 'function') {
onFinish();
}
}
timer.textContent = count;
}, 1000);
};
function someFunc() {
const timer = createTimer();
document.body.append(timer);
startTimer(timer, () => {
console.log('Done');
});
};
someFunc();
The input event fires when the value of an <input>, <select>, or <textarea> element has been changed by user. It does not fire when setting the textContent programmatically.
You can use the MutationObserver API to observe the node change.
const timer = createTimer();
new MutationObserver(() => {
let timer = document.getElementById('display');
if (timer.textContent === '0') {
console.log(true);
};
}).observe(timer, { childList: true });
You could implement the timer with the help of async/await code. It makes the code a lot more cleaner, by having your start and end code in the same function.
const wait = ms => new Promise(resolve => setTimeout(resolve, ms));
function createTimer(name) {
const element = document.createElement("div");
element.classList.add("timer");
document.body.appendChild(element);
element.textContent = name+": -";
return async function(seconds) {
for (let second = seconds; second >= 0; second--) {
element.textContent = name+": "+second;
if (second > 0) {
await wait(1000);
}
}
};
}
async function someFunc() {
const timer = createTimer("First timer");
console.log("first timer started", new Date());
await timer(10);
console.log("timer ended", new Date());
await wait(2500);
console.log("first timer started again", new Date());
await timer(5);
console.log("first timer ended again", new Date());
}
async function someOtherFunc() {
const timer = createTimer("Second timer");
console.log("second timer started", new Date());
await timer(20);
console.log("second timer ended", new Date());
}
someFunc();
someOtherFunc();
.timer {
text-align: center;
font-size: 2em;
font-family: sans-serif;
}
I am looking for a simple throttle in JavaScript. I know libraries like lodash and underscore have it, but only for one function it will be overkill to include any of those libraries.
I was also checking if jQuery has a similar function - could not find.
I have found one working throttle, and here is the code:
function throttle(fn, threshhold, scope) {
threshhold || (threshhold = 250);
var last,
deferTimer;
return function () {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
};
}
The problem with this is: it fires the function once more after the throttle time is complete. So let's assume I made a throttle that fires every 10 seconds on keypress - if I do keypress 2 times, it will still fire the second keypress when 10 seconds are completed. I do not want this behavior.
I would use the underscore.js or lodash source code to find a well tested version of this function.
Here is the slightly modified version of the underscore code to remove all references to underscore.js itself:
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
function throttle(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
if (!options) options = {};
var later = function() {
previous = options.leading === false ? 0 : Date.now();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
};
return function() {
var now = Date.now();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
Please note that this code can be simplified if you don't need all the options that underscore support.
Please find below a very simple and non-configurable version of this function:
function throttle (callback, limit) {
var waiting = false; // Initially, we're not waiting
return function () { // We return a throttled function
if (!waiting) { // If we're not waiting
callback.apply(this, arguments); // Execute users function
waiting = true; // Prevent future invocations
setTimeout(function () { // After a period of time
waiting = false; // And allow future invocations
}, limit);
}
}
}
Edit 1: Removed another reference to underscore, thx to #Zettam 's comment
Edit 2: Added suggestion about lodash and possible code simplification, thx to #lolzery #wowzery 's comment
Edit 3: Due to popular requests, I added a very simple, non-configurable version of the function, adapted from #vsync 's comment
What about this?
function throttle(func, timeFrame) {
var lastTime = 0;
return function () {
var now = Date.now();
if (now - lastTime >= timeFrame) {
func();
lastTime = now;
}
};
}
Simple.
You may be interested in having a look at the source.
callback: takes the function that should be called
limit: number of times that function should be called within the time limit
time: time span to reset the limit count
functionality and usage: Suppose you have an API that allows user to call it 10 times in 1 minute
function throttling(callback, limit, time) {
/// monitor the count
var calledCount = 0;
/// refresh the `calledCount` varialbe after the `time` has been passed
setInterval(function(){ calledCount = 0 }, time);
/// creating a closure that will be called
return function(){
/// checking the limit (if limit is exceeded then do not call the passed function
if (limit > calledCount) {
/// increase the count
calledCount++;
callback(); /// call the function
}
else console.log('not calling because the limit has exceeded');
};
}
////////////////////////////////////////////////////////////
// how to use
/// creating a function to pass in the throttling function
function cb(){
console.log("called");
}
/// calling the closure function in every 100 milliseconds
setInterval(throttling(cb, 3, 1000), 100);
Adding to the discussion here (and for more recent visitors), if the reason for not using the almost de facto throttle from lodash is to have a smaller sized package or bundle, then it's possible to include only throttle in your bundle instead of the entire lodash library. For example in ES6, it would be something like:
import throttle from 'lodash/throttle';
Also, there is a throttle only package from lodash called lodash.throttle which can be used with a simple import in ES6 or require in ES5.
I've just needed a throttle/debounce function for window resize event, and being curious, I also wanted to know what these are and how they work.
I've read multiple blog posts and QAs on SO, but they all seem to overcomplicate this, suggest libraries, or just provide descriptions and not simple plain JS implementations.
I won't provide a description since it's plentiful. So here's my implementation:
function throttle(callback, delay) {
var timeoutHandler = null;
return function () {
if (timeoutHandler == null) {
timeoutHandler = setTimeout(function () {
callback();
timeoutHandler = null;
}, delay);
}
}
}
function debounce(callback, delay) {
var timeoutHandler = null;
return function () {
clearTimeout(timeoutHandler);
timeoutHandler = setTimeout(function () {
callback();
}, delay);
}
}
These might need tweaks (e.g., initially the callback isn't called immediately).
See the difference in action (try resizing the window):
function throttle(callback, delay) {
var timeoutHandler = null;
return function () {
if (timeoutHandler == null) {
timeoutHandler = setTimeout(function () {
callback();
timeoutHandler = null;
}, delay);
}
}
}
function debounce(callback, delay) {
var timeoutHandler = null;
return function () {
clearTimeout(timeoutHandler);
timeoutHandler = setTimeout(function () {
callback();
}, delay);
}
}
var cellDefault = document.querySelector("#cellDefault div");
var cellThrottle = document.querySelector("#cellThrottle div");
var cellDebounce = document.querySelector("#cellDebounce div");
window.addEventListener("resize", function () {
var span = document.createElement("span");
span.innerText = window.innerWidth;
cellDefault.appendChild(span);
cellDefault.scrollTop = cellDefault.scrollHeight;
});
window.addEventListener("resize", throttle(function () {
var span = document.createElement("span");
span.innerText = window.innerWidth;
cellThrottle.appendChild(span);
cellThrottle.scrollTop = cellThrottle.scrollHeight;
}, 500));
window.addEventListener("resize", debounce(function () {
var span = document.createElement("span");
span.innerText = window.innerWidth;
cellDebounce.appendChild(span);
cellDebounce.scrollTop = cellDebounce.scrollHeight;
}, 500));
table {
border-collapse: collapse;
margin: 10px;
}
table td {
border: 1px solid silver;
padding: 5px;
}
table tr:last-child td div {
width: 60px;
height: 200px;
overflow: auto;
}
table tr:last-child td span {
display: block;
}
<table>
<tr>
<td>default</td>
<td>throttle</td>
<td>debounce</td>
</tr>
<tr>
<td id="cellDefault">
<div></div>
</td>
<td id="cellThrottle">
<div></div>
</td>
<td id="cellDebounce">
<div></div>
</td>
</tr>
</table>
JSFiddle
Here's how I implemented throttle function in ES6 in 9LOC, hope it helps
function throttle(func, delay) {
let timeout = null
return function(...args) {
if (!timeout) {
timeout = setTimeout(() => {
func.call(this, ...args)
timeout = null
}, delay)
}
}
}
Click on this link to see how it works.
I've seen a lot of answers here that are way too complex for "a simple throttle in js".
Almost all of the simpler answers just ignore calls made "in throttle" instead of delaying execution to the next interval.
Here's a simple implementation that also handles calls "in throttle":
const throttle = (func, limit) => {
let lastFunc;
let lastRan = Date.now() - (limit + 1); //enforces a negative value on first run
return function(...args) {
const context = this;
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
func.apply(context, args);
lastRan = Date.now();
}, limit - (Date.now() - lastRan)); //negative values execute immediately
}
}
This is almost the exact same implementation for a simple debounce. It just adds a calculation for the timeout delay which requires tracking when the function was last ran. See below:
const debounce = (func, limit) => {
let lastFunc;
return function(...args) {
const context = this;
clearTimeout(lastFunc);
lastFunc = setTimeout(() => {
func.apply(context, args)
}, limit); //no calc here, just use limit
}
}
Simple solution in ES6. Codepen Demo
const handleOnClick = () => {
console.log("hello")
}
const throttle = (func, delay) => {
let timeout = null;
return function (...args) {
if (timeout === null) {
func.apply(this, args);
timeout = setTimeout(() => {
timeout = null;
}, delay)
}
}
}
document.querySelector("#button").addEventListener("click", throttle(handleOnClick, 500))
<button type="button" id="button">Click me</button>
Here's my own version of Vikas post:
throttle: function (callback, limit, time) {
var calledCount = 0;
var timeout = null;
return function () {
if (limit > calledCount) {
calledCount++;
callback();
}
if (!timeout) {
timeout = setTimeout(function () {
calledCount = 0
timeout = null;
}, time);
}
};
}
I find that using setInterval is not a good idea.
With leading and trailing invocations:
const throttle = (fn, ms) => {
let locked = false
return function () {
if (!locked) {
locked = true
fn.apply(this, arguments)
setTimeout(() => {
fn.apply(this, arguments)
locked = false
}, ms)
}
}
}
Test case:
function log({ gender, address }) {
console.log({
name: this.name,
gender,
address,
})
}
const jack = {
name: 'Jack',
log: throttle(log, 3000),
}
Array.from({ length: 5 }, () => jack.log({ gender: 'Male', address: 'LA' }))
I made a npm package with some throttling functions:
npm install function-throttler
throttleAndQueue
Returns a version of your function that can be called at most every W milliseconds, where W is wait. Calls to your func that happen more often than W get queued up to be called every W ms
throttledUpdate
Returns a version of your function that can be called at most every W milliseconds, where W is wait. for calls that happen more often than W the last call will be the one called (last takes precedence)
throttle
limits your function to be called at most every W milliseconds, where W is wait. Calls over W get dropped
There is a library suited for this purpose, it's Backburner.js from Ember.
https://github.com/BackburnerJS/
You'd use it so.
var backburner = new Backburner(["task"]); //You need a name for your tasks
function saySomething(words) {
backburner.throttle("task", console.log.bind(console, words)
}, 1000);
}
function mainTask() {
"This will be said with a throttle of 1 second per word!".split(' ').map(saySomething);
}
backburner.run(mainTask)
This throttle function is build on ES6. Callback functions takes arguments (args), and still it works wrapped with throttle function. Be free to customize delay time according to your app needs. 1 time per 100ms is used for development mode, event "oninput" is just an example for frequent case of its use:
const callback = (...args) => {
console.count('callback throttled with arguments:', args);
};
throttle = (callback, limit) => {
let timeoutHandler = 'null'
return (...args) => {
if (timeoutHandler === 'null') {
timeoutHandler = setTimeout(() => {
callback(...args)
timeoutHandler = 'null'
}, limit)
}
}
}
window.addEventListener('oninput', throttle(callback, 100));
P.S. As #Anshul explained: throttling enforces a maximum number of times a function can be called over time. As in "execute this function at most once every 100 milliseconds."
In below example, try clicking the button multiple times, but the myFunc function would be executed only once in 3 sec.
The function throttle is passed with the function to be executed and the delay.It returns a closure, which is stored in obj.throttleFunc.
Now since obj.throttleFunc stores a closure, the value of isRunning is maintained inside it.
function throttle(func, delay) {
let isRunning;
return function(...args) {
let context = this; // store the context of the object that owns this function
if(!isRunning) {
isRunning = true;
func.apply(context,args) // execute the function with the context of the object that owns it
setTimeout(function() {
isRunning = false;
}, delay);
}
}
}
function myFunc(param) {
console.log(`Called ${this.name} at ${param}th second`);
}
let obj = {
name: "THROTTLED FUNCTION ",
throttleFunc: throttle(myFunc, 3000)
}
function handleClick() {
obj.throttleFunc(new Date().getSeconds());
}
button {
width: 100px;
height: 50px;
font-size: 20px;
}
<button onclick="handleClick()">Click me</button>
If we don't want the context or arguments to be passed, then a simpler
version of this would be as following:
function throttle(func, delay) {
let isRunning;
return function() {
if(!isRunning) {
isRunning = true;
func()
setTimeout(function() {
isRunning = false;
}, delay);
}
}
}
function myFunc() {
console.log('Called');
}
let throttleFunc = throttle(myFunc, 3000);
function handleClick() {
throttleFunc();
}
button {
width: 100px;
height: 50px;
font-size: 20px;
}
<button onclick="handleClick()">Click me</button>
I also want to suggest a simple solution for when there is only 1 function you know you will call (for example: Search)
here is what i did in my project
let throttle;
function search() {
if (throttle) {
clearTimeout(throttle);
}
throttle = setTimeout(() => {
sendSearchReq(str)
}, 500);
}
Search is called on input change event
function throttle(targetFunc, delay){
let lastFunc;
let lastTime;
return function(){
const _this = this;
const args = arguments;
if(!lastTime){
targetFunc.apply(_this, args);
lastTime = Date.now();
} else {
clearTimeout(lastFunc);
lastFunc = setTimeout(function(){
targetFunc.apply(_this, args);
lastTime = Date.now();
}, delay - (Date.now() - lastTime));
}
}
}
Try it :
window.addEventListener('resize', throttle(function() {
console.log('resize!!');
}, 200));
CodeSandbox
const { now } = Date;
export default function throttle(func, frameDuration) {
let timeout = null;
let latest;
const epoch = now();
function getDurationToNextFrame() {
const elapsed = now() - epoch;
const durationSinceLastFrame = elapsed % frameDuration;
return frameDuration - durationSinceLastFrame;
}
function throttled(...args) {
latest = () => {
func.apply(this, args);
};
if (!timeout) {
timeout = setTimeout(() => {
latest();
timeout = null;
}, getDurationToNextFrame());
}
}
return throttled;
}
Simple throttle function -
Note- Keep on clicking on the button , You'll see console log at first on click and then only after every 5 seconds until you're keep clicking.
HTML -
<button id='myid'>Click me</button>
Javascript -
const throttle = (fn, delay) => {
let lastTime = 0;
return (...args) => {
const currentTime = new Date().getTime();
if((currentTime - lastTime) < delay) {
return;
};
lastTime = currentTime;
return fn(...args);
}
};
document.getElementById('myid').addEventListener('click', throttle((e) => {
console.log('I am clicked');
}, 5000));
We can also implement using a flag-
var expensive = function(){
console.log("expensive functionnns");
}
window.addEventListener("resize", throttle(expensive, 500))
function throttle(expensiveFun, limit){
let flag = true;
return function(){
let context = this;
let args = arguments;
if(flag){
expensiveFun.apply(context, args);
flag = false;
setTimeout(function(){
flag = true;
}, limit);
}
}
}
Here is a bit modernized and simplified version of #clément-prévost answer
function throttle(func, wait, options = {}) {
let timeout = null;
let previous = 0;
const later = (...args) => {
previous = options.leading === false ? 0 : Date.now();
func(...args);
};
return (...args) => {
const now = Date.now();
if (!previous && options.leading === false) {
previous = now;
}
const remaining = wait - (now - previous);
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
func(...args);
} else if (options.trailing !== false) {
clearTimeout(timeout);
timeout = setTimeout(() => later(...args), remaining);
}
};
}
function myFunc(a) {
console.log(`Log: ${a} ${this.val}`);
}
const myFuncThrottled = throttle(myFunc.bind({val: 42}), 1234, {leading: true, trailing: true})
myFuncThrottled(1)
myFuncThrottled(2)
myFuncThrottled(3)
function throttle(CB,ms=300,Id='Identifier for the callback(CB)'){
Id = Id || ""+CB
var N = throttle.N = throttle.N || {}; // Static variable N to store all callbacks ids and their status
if( N[Id] ) return; // already in the queue to run
N[Id] = 1; // add it the queue
setTimeout(()=>{
N[Id] = 0; // remove it from the queue
CB(); // finally call the function
}, ms);
}
for(var i=0;i<100;i++){
throttle(e=>console.log("Hi1"),1e3,'F1');
}
// will only output : Hi1
// this function guarantee the callback to run at least once
Some great solutions here already, but I was looking for a modern version with trailing (and optionally leading) executions, with the last passed arguments provided to each function call:
const throttle = (fn, wait=500, leading=true) => {
let prev, timeout, lastargs;
return (...args) => {
lastargs = args;
if (timeout) return;
timeout = setTimeout(() => {
timeout = null;
prev = Date.now();
// let's do this ... we'll release the stored args as we pass them through
fn.apply(this, lastargs.splice(0, lastargs.length));
// some fancy timing logic to allow leading / sub-offset waiting periods
}, leading ? prev && Math.max(0, wait - Date.now() + prev) || 0 : wait);
};
}
Usage:
x = throttle((...args) => console.log(...args));
let n = 0;
x(++n, 'boom');
x(++n, 'boom');
x(++n, 'boom');
if there will be more than one function defining them one by one would not be maintainable so i would suggest use a helper class to keep values for each
class slowDown {
constructor(cb,timeGap){
this.last = 0
this.run = function(){
let current = Date.now(),
shouldRun = (current - this.last) >= timeGap
if(shouldRun){
cb(current - this.last)
this.last = current
}
}
}
}
// example use
const press = new slowDown(timeElapsed => {
// define function here which you wanted to slow down
console.log("pressed after " + timeElapsed + " ms")
},750)
window.addEventListener("keydown",()=>{
press.run()
})
Below is the simplest throttle I could think of, in 13 LOC. It creates a timeout each time the function is called and cancels the old one. The original function is called with the proper context and arguments, as expected.
function throttle(fn, delay) {
var timeout = null;
return function throttledFn() {
window.clearTimeout(timeout);
var ctx = this;
var args = Array.prototype.slice.call(arguments);
timeout = window.setTimeout(function callThrottledFn() {
fn.apply(ctx, args);
}, delay);
}
}
// try it out!
window.addEventListener('resize', throttle(function() {
console.log('resize!!');
}, 200));
I'm trying to figure out a way to emulate AS3's Timer class.
If you're not familiar, one of the cool things you can do is add duration to the timer even if it's already running. This functionality has a lot of very nice uses.
Anyone have any thoughts on doing this in js?
I'm not familiar with this class, but you can easily create something similar in JavaScript:
function Timer(callback, time) {
this.setTimeout(callback, time);
}
Timer.prototype.setTimeout = function(callback, time) {
var self = this;
if(this.timer) {
clearTimeout(this.timer);
}
this.finished = false;
this.callback = callback;
this.time = time;
this.timer = setTimeout(function() {
self.finished = true;
callback();
}, time);
this.start = Date.now();
}
Timer.prototype.add = function(time) {
if(!this.finished) {
// add time to time left
time = this.time - (Date.now() - this.start) + time;
this.setTimeout(this.callback, time);
}
}
Usage:
var timer = new Timer(function() { // init timer with 5 seconds
alert('foo');
}, 5000);
timer.add(2000); // add two seconds
Clear the timeout, then set a new timeout to the new desired end time.
Wrap the function with another one, and when the timer runs out, test to see if an extra time variable has been set. If it has, start again with the new time, otherwise execute the function.
A quickly hacked together script might look like:
function test() {
tim = new timer(function () { alert('hello'); }, 5000);
}
function extend() {
if (tim) { tim.addTime(5000); }
}
function timer(func, time) {
var self = this,
execute = function () {
self.execute()
};
this.func = func;
this.extraTime = 0;
setTimeout(execute, time);
};
timer.prototype.execute = function () {
var self = this,
execute = function () {
self.execute()
};
if (this.extraTime) {
setTimeout(execute, this.extraTime);
this.extraTime = 0;
} else {
this.func();
}
};
timer.prototype.addTime = function (time) {
this.extraTime += time;
}
<input type="button" value="Start" onclick="test()">
<input type="button" value="Extend" onclick="extend()">
There you go hope it helps :) just call setInterval with the time you want to have.
Edit: added stop and start in case you want to stop your loop :p
function Timer(defaultInterval, callback){
var interval = defaultInterval;
var running = true;
function loop(){
callback();
if(running){
setTimeout(function(){
loop();
}, interval);
}
}
loop();
return {
setInterval: function(newInterval){
interval = newInterval;
},
stop: function(){
running = false;
},
start: function(){
if(running===false){
running = true;
loop();
}
},
add: function(milliToAdd){
interval += milliToAdd*1;
}
}
}
var myTimer = Timer(250, function() { process code here });
myTimer.setInterval(1000); // sets interval to 1 second
myTimer.stop(); // stops the function
myTimer.start(); // re-starts the loop;
function Timer(func, delay) {
var done = false;
var callback = function() {
done = true;
return func();
};
var startTime = Date.now();
var timeout = setTimeout(callback, delay);
this.add = function(ms) {
if (!done) {
this.cancel();
delay = delay - (Date.now() - startTime) + ms;
timeout = setTimeout(callback, delay);
}
};
this.cancel = function() {
clearTimeout(timeout);
};
this.immediately = function() {
if (!done) {
this.cancel();
callback();
}
};
};
quick test in the console
start = Date.now();
t = new Timer(function() { console.log(Date.now() - start); }, 1000);
t.add(200);
start = Date.now();
t = new Timer(function() { console.log(Date.now() - start); }, 1000000);
t.immediately();
t.immediately();
you can add negative times too.
start = Date.now();
t = new Timer(function() { console.log(Date.now() - start); }, 1000);
t.add(-200);
Here's my shot. It keeps track of when the timer was set, and adds the difference to the specified time when you add time.
var Timer = {
set: function(p_function, p_time)
{
var d = new Date();
this.timeStarted = d.getTime();
this.func = p_function;
this.timeout = setTimeout(p_function, p_time);
console.log('timer started at ' + (this.timeStarted / 1000) + ' seconds');
},
add: function(p_time)
{
var d = new Date(),
diff = d.getTime() - this.timeStarted,
newTime = diff + p_time;
if (this.timeout)
{
clearTimeout(this.timeout);
}
this.timeout = setTimeout(this.func, newTime);
this.timeStarted = d.getTime();
}
};
var myTimer = Object.create(Timer);
myTimer.set(function() {
var d = new Date();
console.log('Timer fired at ' + (d.getTime() / 1000) + ' seconds');
}, 10000);
setTimeout(function () {
myTimer.add(5000);
}, 5000);
Here's a jsFiddle
Please note that due to overhead of calculation and function calls, this may be a couple milliseconds off.
I decided to throw my little rubber ducky into the pool.
var setTimeout2 = function(callback, delay) {
this.complete = false;
this.callback = callback;
this.delay = delay;
this.timeout = false;
this.dotimeout = function() {
this.timeout = setTimeout(function() {
this.complete = true;
this.callback.call();
}, this.delay);
};
this.start = Date.now();
this.add = function(delay) {
if (!this.complete) {
this.delay = this.delay - (Date.now() - this.start) + delay;
clearTimeout(this.timeout);
this.dotimeout.call();
}
};
return this;
};
usage
var start = Date.now();
var to = setTimeout2(function() {
document.write(Date.now() - start);
}, 3000);
to.add(3000);
similar to this approach but a little more compact / no proto