I have a situation where multiple setTimeout() calls (typical length: 100ms to 1 sec) are active and should have gone off, but they do not fire. A Chrome (Mac) debugger profile shows "idle" during this (potentially infinite) period. The profile shows that nothing is going on. No loops. No code running. No garbage collection. No activity whatsoever. The viewport is active and has focus. After I wait (potentially an infinite time), when I "do something else", like mouseover some unrelated element -- as simple as a :hover -- the logjam breaks and the queued setTimeout()s all fire.
When I set breakpoints in the setTimeout() handler functions after this "freeze" occurs, they get hit in sequence when the logjam breaks, just as you would expect.
Unfortunately the replication path is difficult. So far, creating simpler test cases just makes replication even more difficult or, ultimately, impossible.
Most of the chatter around setTimeout() "issues" is from people who don't understand the single-thread nature of jscript, etc., so it is not helpful. Let me repeat: the timers are queued and should have fired. The browser is idle, as proved by the profiler. The timers DO fire ultimately, but only after mouse activity occurs. This behavior seems very wrong to me. If the browser is idle, and there are events in the queue, they should fire.
Has anyone seen behavior like this? Have I stumbled across a way to lock up the event dispatcher? Maybe I'm missing something.
Update: Cannot replicate on Windows 7.
Update 2: Restarted Chrome on Mac, can no longer replicate. So, worst possible outcome: no answer as to why it happened, why it kept happening, why it didn't happen reliably, why it went away, and why it won't happen any more.
I had a similar issue recently and discovered that since version 47, chromium folks have decided to not honour setTimeout when they believe this is 'detrimental to the majority of users'. Basically they've deprecated the setTimeout API (asking no one, as usual).
Here is bug 570845 where people discovered about this. There are a number of other bugs and discussion threads regarding the issue.
The fallback is to emulate setTimeout using requestAnimationFrame.
Here is a proof of concept:
'use strict'
var PosfScheduler = ( function () {
/*
* Constructor.
* Initiate the collection of timers and start the poller.
*/
function PosfScheduler () {
this.timers = {};
this.counter = 0;
this.poll = function () {
var scheduler = this;
var timers = scheduler.timers;
var now = Date.now();
for ( var timerId in timers ) {
var timer = timers[timerId];
if ( now - timer.submitDate >= timer.delay ) {
if ( timer.permanent === true ) {
timer.submitDate = now;
} else {
delete timers[timer.id];
}
timer.func.apply.bind( timer.func, timer.funcobj, timer.funcargs ).apply();
}
}
requestAnimationFrame( scheduler.poll.bind(scheduler) );
};
this.poll();
};
/*
* Adding a timer.
* A timer can be
* - an interval (arg[0]: true) - a recurrent timeout
* - a simple timeout (arg[0]: false)
*/
PosfScheduler.prototype.addTimer = function () {
var id = this.counter++;
var permanent = arguments[0] ;
var func = arguments[1] ;
var delay = arguments[2] ;
var funcobj = arguments[3] ;
var funcargs = Array.prototype.slice.call(arguments).slice(4);
var submitDate = Date.now() ;
var timer = {
id: id,
permanent: permanent,
func: func,
delay: delay,
funcargs: funcargs,
submitDate: submitDate,
}
this.timers[id] = timer;
return timer;
};
/*
* Replacement for setTimeout
* Similar signature:
* setTimeout ( function, delay [obj,arg1...] )
*/
PosfScheduler.prototype.setTimeout = function () {
var args = Array.prototype.slice.call(arguments);
return this.addTimer.apply.bind( this.addTimer, this, [false].concat(args) ).apply();
};
/*
* Replacement for setInterval - Untested for now.
* Signature:
* setInterval ( function, delay [obj,arg1...] )
*/
PosfScheduler.prototype.setInterval = function () {
var args = Array.prototype.slice.call(arguments);
return this.addTimer.apply.bind( this.addTimer, this, [true].concat(args) ).apply();
};
PosfScheduler.prototype.cancelTimeout = function ( timer ) {
delete this.timers[timer.id];
};
/*
* Don't want to leave all these schedulers hogging the javascript thread.
*/
PosfScheduler.prototype.shutdown = function () {
delete this;
};
return PosfScheduler;
})();
var scheduler = new PosfScheduler();
var initTime = Date.now();
var timer1 = scheduler.setTimeout ( function ( init ) {
console.log ('executing function1 (should appear) after ' + String ( Date.now() - init ) + 'ms!' );
}, 200, null, initTime );
var timer2 = scheduler.setTimeout ( function ( init ) {
console.log ('executing function2 afte: ' + String ( Date.now() - init ) + 'ms!' );
}, 300, null, initTime );
var timer3 = scheduler.setTimeout ( function ( init ) {
console.log ('executing function3 (should not appear) after ' + String ( Date.now() - init ) + 'ms!' );
}, 1000, null, initTime );
var timer4 = scheduler.setTimeout ( function ( init, sched, timer ) {
console.log ('cancelling timer3 after ' + String ( Date.now() - init ) + 'ms!' );
sched.cancelTimeout ( timer3 );
}, 500, null, initTime, scheduler, timer3 );
var timer5 = scheduler.setInterval ( function ( init, sched, timer ) {
console.log ('periodic after ' + String ( Date.now() - init ) + 'ms!' );
}, 400, null, initTime, scheduler, timer3 );
var timer6 = scheduler.setTimeout ( function ( init, sched, timer ) {
console.log ('cancelling periodic after ' + String ( Date.now() - init ) + 'ms!' );
sched.cancelTimeout ( timer5 );
}, 900, null, initTime, scheduler, timer5 );
i want to build a javascript class to register several different functions to execute a single common callback, all registered functions should execute asynchronously and then once all of them are finished, it should execute the defined callback function.
We should also be able to define a maximum time before the callback function executes. For example if we define that as 3000 and it takes more than 3000 ms for all registered functions to return, it should proceed to execute callback function even though the ajax functions have not finished retuning.
To be clear, this code needs to be flexible, standalone and reusable
To assume that any function that we register will implement a call to a function that we define at some point within it mark its completion. for eg. at the end of my function i'll enter myClass.markDone() to let the class know the function has completed executing
Is it possible using javascript or with angular.js and without jquery?
To achieve this take a look at these angular built in modules:
https://docs.angularjs.org/api/ng/service/$q
https://docs.angularjs.org/api/ng/service/$timeout
Here is an example implementation on plunkr:
qAllWithTimeout([
makePromise(function(callback) {
// Mock async call 1
setTimeout(callback, 200);
}),
makePromise(function(callback) {
// Mock async call 2
setTimeout(callback, 500);
}),
makePromise(function(callback) {
// Long running mock async call 2
setTimeout(callback, 10500);
})
], 3000)
.then(function() {
$scope.state = 'ready';
})
http://plnkr.co/edit/hNo9kJmKIR4hEoNk9pP2?p=preview
iam not sure this will work, am not tested. this may give you some idea.
function TestFun(callback, timeout){
this._callback = callback;
this._timeout = timeout;
this._forceFinish = false;
this.fun_list = [];
this.RegisterFun = function(fun){
this.fun_list.push(fun);
};
this.startCount = -1;
this.finishCount = 0;
this.timeOutFunction= function(){
this.startCount++;
fun_list[this.startCount]();
this.commonCallback();
}
this.Start = function(){
for(var i=0; i <this.fun_list.length ;i++){
setTimeout( this.timeOutFunction, 0);
}
setTimeout( this.watcherFun, 1 );
};
this.commonCallback = function(){
if( this._forceFinish){
this._callback();
}else{
this.finishCount++;
if(this.finishCount == this.fun_list.length ){
this._callback();
}
}
}
this.watcherFun = function(){
if( this._timeout !=0 ){
this._timeout-1;
setTimeout( this.watcherFun, 1 );
}else{
this._forceFinish = true;
this.commonCallback();
}
}
}
//usage
var funMngr = new TestFun(finalFun, 60 * 1000);
funMngr.RegisterFun ( fun1 );
funMngr.RegisterFun ( fun2 );
funMngr.RegisterFun ( fun3 );
funMngr.Start();
function finalFun(){
alert("all functions executed" );
}
I asked for clarification in a comment already, but I went ahead and started drafting a solution. Here's what I came up with, hope this is what you meant:
var AsyncBatch = function() {
this.runnables = new Set();
};
AsyncBatch.prototype = {
runnables: null,
timeoutId: 0,
add: function(runnable) {
this.runnables.add(runnable);
},
start: function() {
this.timeoutId = window.setTimeout(this.timeout.bind(this), 3000);
let promises = [];
for (let runnable of this.runnables) {
promises.add(new Promise(resolve => {
runnable(resolve);
}));
}
Promise.all(promises).then(() => this.allDone());
},
allDone: function() {
if (this.timeoutId == 0) return;
window.clearTimeout(this.timeoutId);
this.finish();
},
timeout: function() {
this.timeoutId = 0;
this.finish();
},
finish: function() {
// Do something here when all registered callbacks are finished OR the batch timed out
},
};
Here's how you would use this:
Create an instance of AsyncBatch.
Call .add() as many times as you want, passing a function. This functions should expect a single parameter, which is the callback it should invoke when its job is done.
Call .start() on the AsyncBatch instance. It will execute all runnables asynchronously, as well as start a timer.
If the runnables all finish before the timer runs out, .allDone will cancel the timer and execute .finish().
If the timer runs out before the runnables, it executes .finish(), and sets the timerId to 0 so that .finish() won't be called again when the runnables all finish.
Let's assume that I have the timeout ID returned from setTimeout or setInterval.
Can I get, in some way, the original function or code, associated with it?
Something like this:
var timer_id = setTimeout(function() {
console.log('Hello Stackoverflowers!');
}, 100000);
var fn = timer_id.get_function(); // desired method
fn(); // output: 'Hello Stackoverflowers!'
You can put a wrapper around setTimeout - I just threw this one together (after a few iterations of testing...)
(function() {
var cache = {};
var _setTimeout = window.setTimeout;
var _clearTimeout = window.clearTimeout;
window.setTimeout = function(fn, delay) {
var id = _setTimeout(function() {
delete cache[id]; // ensure the map is cleared up on completion
fn();
}, delay);
cache[id] = fn;
return id;
}
window.clearTimeout = function(id) {
delete cache[id];
_clearTimeout(id);
}
window.getTimeout = function(id) {
return cache[id];
}
})();
NB: this won't work if you use a string for the callback. But no one does that, do they..?
Nor does it support passing the ES5 additional parameters to the callback function, although this would be easy to support.
var timeouts = {}; // hold the data
function makeTimeout (func, interval) {
var run = function(){
timeouts[id] = undefined;
func();
}
var id = window.setTimeout(run, interval);
timeouts[id] = func;
return id;
}
function removeTimeout (id) {
window.clearTimeout(id);
timeouts[id]=undefined;
}
function doTimeoutEarly (id) {
func = timeouts[id];
removeTimeout(id);
func();
}
var theId = makeTimeout( function(){ alert("here"); }, 10000);
console.log((timeouts[theId] || "").toString());
timeouts[theId](); // run function immediately, will still run with timer
You can store each timeout function in an object so that you can retrieve it later.
var timeout_funcs = {};
function addTimeout(func,time) {
var id = window.setTimeout(func,time);
timeout_funcs[id] = func;
return id;
}
function getTimeout(id) {
if(timeout_funcs[id])
return timeout_funcs[id];
else
return null;
}
function delTimeout(id) {
if(timeout_funcs[id]) {
window.clearTimeout(timeout_funcs[id]);
delete timeout_funcs[id];
}
}
the IDs returned from setTimeout/setInterval are just numbers, they have no properties or methods other than those that every other number would have. If you want to get that function, you can declare it first instead of using an anonymous:
var myFunc = function() {
console.log('Hello Stackoverflowers!');
};
var timer_id = setTimeout(myFunc, 100000);
myFunc(); // output: 'Hello Stackoverflowers!'
clearTimeout(timer_id); // unless you want it to fire twice
I’ve got a search field. Right now it searches for every keyup. So if someone types “Windows”, it will make a search with AJAX for every keyup: “W”, “Wi”, “Win”, “Wind”, “Windo”, “Window”, “Windows”.
I want to have a delay, so it only searches when the user stops typing for 200 ms.
There is no option for this in the keyup function, and I have tried setTimeout, but it didn’t work.
How can I do that?
I use this small function for the same purpose, executing a function after the user has stopped typing for a specified amount of time or in events that fire at a high rate, like resize:
function delay(callback, ms) {
var timer = 0;
return function() {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
callback.apply(context, args);
}, ms || 0);
};
}
// Example usage:
$('#input').keyup(delay(function (e) {
console.log('Time elapsed!', this.value);
}, 500));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for="input">Try it:
<input id="input" type="text" placeholder="Type something here..."/>
</label>
How it works:
The delay function will return a wrapped function that internally handles an individual timer, in each execution the timer is restarted with the time delay provided, if multiple executions occur before this time passes, the timer will just reset and start again.
When the timer finally ends, the callback function is executed, passing the original context and arguments (in this example, the jQuery's event object, and the DOM element as this).
UPDATE 2019-05-16
I have re-implemented the function using ES5 and ES6 features for modern environments:
function delay(fn, ms) {
let timer = 0
return function(...args) {
clearTimeout(timer)
timer = setTimeout(fn.bind(this, ...args), ms || 0)
}
}
The implementation is covered with a set of tests.
For something more sophisticated, give a look to the jQuery Typewatch plugin.
If you want to search after the type is done use a global variable to hold the timeout returned from your setTimout call and cancel it with a clearTimeout if it hasn't yet happend so that it won't fire the timeout except on the last keyup event
var globalTimeout = null;
$('#id').keyup(function(){
if(globalTimeout != null) clearTimeout(globalTimeout);
globalTimeout =setTimeout(SearchFunc,200);
}
function SearchFunc(){
globalTimeout = null;
//ajax code
}
Or with an anonymous function :
var globalTimeout = null;
$('#id').keyup(function() {
if (globalTimeout != null) {
clearTimeout(globalTimeout);
}
globalTimeout = setTimeout(function() {
globalTimeout = null;
//ajax code
}, 200);
}
Another slight enhancement on CMS's answer. To easily allow for separate delays, you can use the following:
function makeDelay(ms) {
var timer = 0;
return function(callback){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
};
If you want to reuse the same delay, just do
var delay = makeDelay(250);
$(selector1).on('keyup', function() {delay(someCallback);});
$(selector2).on('keyup', function() {delay(someCallback);});
If you want separate delays, you can do
$(selector1).on('keyup', function() {makeDelay(250)(someCallback);});
$(selector2).on('keyup', function() {makeDelay(250)(someCallback);});
You could also look at underscore.js, which provides utility methods like debounce:
var lazyLayout = _.debounce(calculateLayout, 300);
$(window).resize(lazyLayout);
Explanation
Use a variable to store the timeout function. Then use clearTimeout() to clear this variable of any active timeout functions, and then use setTimeout() to set the active timeout function again. We run clearTimeout() first, because if a user is typing "hello", we want our function to run shortly after the user presses the "o" key (and not once for each letter).
Working Demo
Super simple approach, designed to run a function after a user has finished typing in a text field...
$(document).ready(function(e) {
var timeout;
var delay = 2000; // 2 seconds
$('.text-input').keyup(function(e) {
$('#status').html("User started typing!");
if(timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(function() {
myFunction();
}, delay);
});
function myFunction() {
$('#status').html("Executing function for user!");
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Status: <span id="status">Default Status</span><br>
<textarea name="text-input" class="text-input"></textarea>
Based on the answer of CMS, I made this :
Put the code below after include jQuery :
/*
* delayKeyup
* http://code.azerti.net/javascript/jquery/delaykeyup.htm
* Inspired by CMS in this post : http://stackoverflow.com/questions/1909441/jquery-keyup-delay
* Written by Gaten
* Exemple : $("#input").delayKeyup(function(){ alert("5 secondes passed from the last event keyup."); }, 5000);
*/
(function ($) {
$.fn.delayKeyup = function(callback, ms){
var timer = 0;
$(this).keyup(function(){
clearTimeout (timer);
timer = setTimeout(callback, ms);
});
return $(this);
};
})(jQuery);
And simply use like this :
$('#input').delayKeyup(function(){ alert("5 secondes passed from the last event keyup."); }, 5000);
Careful : the $(this) variable in the function passed as a parameter does not match input
jQuery:
var timeout = null;
$('#input').keyup(function() {
clearTimeout(timeout);
timeout = setTimeout(() => {
console.log($(this).val());
}, 1000);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<input type="text" id="input" placeholder="Type here..."/>
Pure Javascript:
let input = document.getElementById('input');
let timeout = null;
input.addEventListener('keyup', function (e) {
clearTimeout(timeout);
timeout = setTimeout(function () {
console.log('Value:', input.value);
}, 1000);
});
<input type="text" id="input" placeholder="Type here..."/>
Delay Multi Function Calls using Labels
This is the solution i work with. It will delay the execution on ANY function you want. It can be the keydown search query, maybe the quick click on previous or next buttons ( that would otherwise send multiple request if quickly clicked continuously , and be not used after all). This uses a global object that stores each execution time, and compares it with the most current request.
So the result is that only that last click / action will actually be called, because those requests are stored in a queue, that after the X milliseconds is called if no other request with the same label exists in the queue!
function delay_method(label,callback,time){
if(typeof window.delayed_methods=="undefined"){window.delayed_methods={};}
delayed_methods[label]=Date.now();
var t=delayed_methods[label];
setTimeout(function(){ if(delayed_methods[label]!=t){return;}else{ delayed_methods[label]=""; callback();}}, time||500);
}
You can set your own delay time ( its optional, defaults to 500ms). And send your function arguments in a "closure fashion".
For example if you want to call the bellow function:
function send_ajax(id){console.log(id);}
To prevent multiple send_ajax requests, you delay them using:
delay_method( "check date", function(){ send_ajax(2); } ,600);
Every request that uses the label "check date" will only be triggered if no other request is made in the 600 miliseconds timeframe. This argument is optional
Label independency (calling the same target function) but run both:
delay_method("check date parallel", function(){send_ajax(2);});
delay_method("check date", function(){send_ajax(2);});
Results in calling the same function but delay them independently because of their labels being different
If someone like to delay the same function, and without external variable he can use the next script:
function MyFunction() {
//Delaying the function execute
if (this.timer) {
window.clearTimeout(this.timer);
}
this.timer = window.setTimeout(function() {
//Execute the function code here...
}, 500);
}
This function extends the function from Gaten's answer a bit in order to get the element back:
$.fn.delayKeyup = function(callback, ms){
var timer = 0;
var el = $(this);
$(this).keyup(function(){
clearTimeout (timer);
timer = setTimeout(function(){
callback(el)
}, ms);
});
return $(this);
};
$('#input').delayKeyup(function(el){
//alert(el.val());
// Here I need the input element (value for ajax call) for further process
},1000);
http://jsfiddle.net/Us9bu/2/
I'm surprised that nobody mention the problem with multiple input in CMS's very nice snipped.
Basically, you would have to define delay variable individually for each input. Otherwise if sb put text to first input and quickly jump to other input and start typing, callback for the first one WON'T be called!
See the code below I came with based on other answers:
(function($) {
/**
* KeyUp with delay event setup
*
* #link http://stackoverflow.com/questions/1909441/jquery-keyup-delay#answer-12581187
* #param function callback
* #param int ms
*/
$.fn.delayKeyup = function(callback, ms){
$(this).keyup(function( event ){
var srcEl = event.currentTarget;
if( srcEl.delayTimer )
clearTimeout (srcEl.delayTimer );
srcEl.delayTimer = setTimeout(function(){ callback( $(srcEl) ); }, ms);
});
return $(this);
};
})(jQuery);
This solution keeps setTimeout reference within input's delayTimer variable. It also passes reference of element to callback as fazzyx suggested.
Tested in IE6, 8(comp - 7), 8 and Opera 12.11.
This worked for me where I delay the search logic operation and make a check if the value is same as entered in text field. If value is same then I go ahead and perform the operation for the data related to search value.
$('#searchText').on('keyup',function () {
var searchValue = $(this).val();
setTimeout(function(){
if(searchValue == $('#searchText').val() && searchValue != null && searchValue != "") {
// logic to fetch data based on searchValue
}
else if(searchValue == ''){
// logic to load all the data
}
},300);
});
Delay function to call up on every keyup.
jQuery 1.7.1 or up required
jQuery.fn.keyupDelay = function( cb, delay ){
if(delay == null){
delay = 400;
}
var timer = 0;
return $(this).on('keyup',function(){
clearTimeout(timer);
timer = setTimeout( cb , delay );
});
}
Usage: $('#searchBox').keyupDelay( cb );
From ES6, one can use arrow function syntax as well.
In this example, the code delays keyup event for 400ms after users finish typeing before calling searchFunc make a query request.
const searchbar = document.getElementById('searchBar');
const searchFunc = // any function
// wait ms (milliseconds) after user stops typing to execute func
const delayKeyUp = (() => {
let timer = null;
const delay = (func, ms) => {
timer ? clearTimeout(timer): null
timer = setTimeout(func, ms)
}
return delay
})();
searchbar.addEventListener('keyup', (e) => {
const query = e.target.value;
delayKeyUp(() => {searchFunc(query)}, 400);
})
Updated Typescript version:
const delayKeyUp = (() => {
let timer: NodeJS.Timeout;
return (func: Function, ms: number) => {
timer ? clearTimeout(timer) : null;
timer = setTimeout(() => func(), ms);
};
})();
This is a solution along the lines of CMS's, but solves a few key issues for me:
Supports multiple inputs, delays can run concurrently.
Ignores key events that didn't changed the value (like Ctrl, Alt+Tab).
Solves a race condition (when the callback is executed and the value already changed).
var delay = (function() {
var timer = {}
, values = {}
return function(el) {
var id = el.form.id + '.' + el.name
return {
enqueue: function(ms, cb) {
if (values[id] == el.value) return
if (!el.value) return
var original = values[id] = el.value
clearTimeout(timer[id])
timer[id] = setTimeout(function() {
if (original != el.value) return // solves race condition
cb.apply(el)
}, ms)
}
}
}
}())
Usage:
signup.key.addEventListener('keyup', function() {
delay(this).enqueue(300, function() {
console.log(this.value)
})
})
The code is written in a style I enjoy, you may need to add a bunch of semicolons.
Things to keep in mind:
A unique id is generated based on the form id and input name, so they must be defined and unique, or you could adjust it to your situation.
delay returns an object that's easy to extend for your own needs.
The original element used for delay is bound to the callback, so this works as expected (like in the example).
Empty value is ignored in the second validation.
Watch out for enqueue, it expects milliseconds first, I prefer that, but you may want to switch the parameters to match setTimeout.
The solution I use adds another level of complexity, allowing you to cancel execution, for example, but this is a good base to build on.
Combining CMS answer with Miguel's one yields a robust solution allowing concurrent delays.
var delay = (function(){
var timers = {};
return function (callback, ms, label) {
label = label || 'defaultTimer';
clearTimeout(timers[label] || 0);
timers[label] = setTimeout(callback, ms);
};
})();
When you need to delay different actions independently, use the third argument.
$('input.group1').keyup(function() {
delay(function(){
alert('Time elapsed!');
}, 1000, 'firstAction');
});
$('input.group2').keyup(function() {
delay(function(){
alert('Time elapsed!');
}, 1000, '2ndAction');
});
Building upon CMS's answer here's new delay method which preserves 'this' in its usage:
var delay = (function(){
var timer = 0;
return function(callback, ms, that){
clearTimeout (timer);
timer = setTimeout(callback.bind(that), ms);
};
})();
Usage:
$('input').keyup(function() {
delay(function(){
alert('Time elapsed!');
}, 1000, this);
});
If you want to do something after a period of time and reset that timer after a specific event like keyup, the best solution is made with clearTimeout and setTimeout methods:
// declare the timeout variable out of the event listener or in the global scope
var timeout = null;
$(".some-class-or-selector-to-bind-event").keyup(function() {
clearTimeout(timout); // this will clear the recursive unneccessary calls
timeout = setTimeout(() => {
// do something: send an ajax or call a function here
}, 2000);
// wait two seconds
});
Use
mytimeout = setTimeout( expression, timeout );
where expression is the script to run and timeout is the time to wait in milliseconds before it runs - this does NOT hault the script, but simply delays execution of that part until the timeout is done.
clearTimeout(mytimeout);
will reset/clear the timeout so it does not run the script in expression (like a cancel) as long as it has not yet been executed.
Based on the answer of CMS, it just ignores the key events that doesn't change value.
var delay = (function(){
var timer = 0;
return function(callback, ms){
clearTimeout (timer);
timer = setTimeout(callback, ms);
};
})();
var duplicateFilter=(function(){
var lastContent;
return function(content,callback){
content=$.trim(content);
if(content!=lastContent){
callback(content);
}
lastContent=content;
};
})();
$("#some-input").on("keyup",function(ev){
var self=this;
delay(function(){
duplicateFilter($(self).val(),function(c){
//do sth...
console.log(c);
});
}, 1000 );
})
User lodash javascript library and use _.debounce function
changeName: _.debounce(function (val) {
console.log(val)
}, 1000)
Use the bindWithDelay jQuery plugin:
element.bindWithDelay(eventType, [ eventData ], handler(eventObject), timeout, throttle)
var globalTimeout = null;
$('#search').keyup(function(){
if(globalTimeout != null) clearTimeout(globalTimeout);
globalTimeout =setTimeout(SearchFunc,200);
});
function SearchFunc(){
globalTimeout = null;
console.log('Search: '+$('#search').val());
//ajax code
};
Here is a suggestion I have written that takes care of multiple input in your form.
This function gets the Object of the input field, put in your code
function fieldKeyup(obj){
// what you want this to do
} // fieldKeyup
This is the actual delayCall function, takes care of multiple input fields
function delayCall(obj,ms,fn){
return $(obj).each(function(){
if ( typeof this.timer == 'undefined' ) {
// Define an array to keep track of all fields needed delays
// This is in order to make this a multiple delay handling
function
this.timer = new Array();
}
var obj = this;
if (this.timer[obj.id]){
clearTimeout(this.timer[obj.id]);
delete(this.timer[obj.id]);
}
this.timer[obj.id] = setTimeout(function(){
fn(obj);}, ms);
});
}; // delayCall
Usage:
$("#username").on("keyup",function(){
delayCall($(this),500,fieldKeyup);
});
Take a look at the autocomplete plugin. I know that it allows you to specify a delay or a minimum number of characters. Even if you don't end up using the plugin, looking through the code will give you some ideas on how to implement it yourself.
Well, i also made a piece of code for limit high frequency ajax request cause by Keyup / Keydown. Check this out:
https://github.com/raincious/jQueue
Do your query like this:
var q = new jQueue(function(type, name, callback) {
return $.post("/api/account/user_existed/", {Method: type, Value: name}).done(callback);
}, 'Flush', 1500); // Make sure use Flush mode.
And bind event like this:
$('#field-username').keyup(function() {
q.run('Username', this.val(), function() { /* calling back */ });
});
Saw this today a little late but just want to put this here in case someone else needed. just separate the function to make it reusable. the code below will wait 1/2 second after typing stop.
var timeOutVar
$(selector).on('keyup', function() {
clearTimeout(timeOutVar);
timeOutVar= setTimeout(function(){ console.log("Hello"); }, 500);
});
// Get an global variable isApiCallingInProgress
// check isApiCallingInProgress
if (!isApiCallingInProgress) {
// set it to isApiCallingInProgress true
isApiCallingInProgress = true;
// set timeout
setTimeout(() => {
// Api call will go here
// then set variable again as false
isApiCallingInProgress = false;
}, 1000);
}
What is the most recommended/best way to stop multiple instances of a setTimeout function from being created (in javascript)?
An example (psuedo code):
function mouseClick()
{
moveDiv("div_0001", mouseX, mouseY);
}
function moveDiv(objID, destX, destY)
{
//some code that moves the div closer to destination
...
...
...
setTimeout("moveDiv(objID, destX, destY)", 1000);
...
...
...
}
My issue is that if the user clicks the mouse multiple times, I have multiple instances of moveDiv() getting called.
The option I have seen is to create a flag, that only allows the timeout to be called if no other instance is available...is that the best way to go?
I hope that makes it clear....
when you call settimeout, it returns you a variable "handle" (a number, I think)
if you call settimeout a second time, you should first
clearTimeout( handle )
then:
handle = setTimeout( ... )
to help automate this, you might use a wrapper that associates timeout calls with a string (i.e. the div's id, or anything you want), so that if there's a previous settimeout with the same "string", it clears it for you automatically before setting it again,
You would use an array (i.e. dictionary/hashmap) to associate strings with handles.
var timeout_handles = []
function set_time_out( id, code, time ) /// wrapper
{
if( id in timeout_handles )
{
clearTimeout( timeout_handles[id] )
}
timeout_handles[id] = setTimeout( code, time )
}
There are of course other ways to do this ..
I would do it this way:
// declare an array for all the timeOuts
var timeOuts = new Array();
// then instead of a normal timeOut call do this
timeOuts["uniqueId"] = setTimeout('whateverYouDo("fooValue")', 1000);
// to clear them all, just call this
function clearTimeouts() {
for (key in timeOuts) {
clearTimeout(timeOuts[key]);
}
}
// clear just one of the timeOuts this way
clearTimeout(timeOuts["uniqueId"]);
var timeout1 = window.setTimeout('doSomething();', 1000);
var timeout2 = window.setTimeout('doSomething();', 1000);
var timeout3 = window.setTimeout('doSomething();', 1000);
// to cancel:
window.clearTimeout(timeout1);
window.clearTimeout(timeout2);
window.clearTimeout(timeout3);
I haven't tested any of this, and just cut this up in the editor here. Might work, might not, hopefully will be food for thought though.
var Timeout = {
_timeouts: {},
set: function(name, func, time){
this.clear(name);
this._timeouts[name] = {pending: true, func: func};
var tobj = this._timeouts[name];
tobj.timeout = setTimeout(function()
{
/* setTimeout normally passes an accuracy report on some browsers, this just forwards that. */
tobj.func.call(arguments);
tobj.pending = false;
}, time);
},
hasRun: function(name)
{
if( this._timeouts[name] )
{
return !this._timeouts[name].pending;
}
return -1; /* Whut? */
},
runNow: function(name)
{
if( this._timeouts[name] && this.hasRun(name)===false )
{
this._timeouts[name].func(-1); /* fake time. *shrug* */
this.clear(name);
}
}
clear: function(name)
{
if( this._timeouts[name] && this._timeouts[name].pending )
{
clearTimeout(this._timeouts[name].timeout);
this._timeouts[name].pending = false;
}
}
};
Timeout.set("doom1", function(){
if( Timeout.hasRun("doom2") === true )
{
alert("OMG, it has teh run");
}
}, 2000 );
Timeout.set("doom2", function(){
/* NooP! */
}, 1000 );
Successive calls with the same identifier will cancel the previous call.
You could store multiple flags in a lookup-table (hash) using objID as a key.
var moving = {};
function mouseClick()
{
var objID = "div_0001";
if (!moving[objID])
{
moving[objID] = true;
moveDiv("div_0001", mouseX, mouseY);
}
}
You can avoid a global or lesser variable by using a property within the function. This works well if the function is only used for this specific context.
function set_time_out( id, code, time ) /// wrapper
{
if(typeof this.timeout_handles == 'undefined') this.timeout_handles = [];
if( id in this.timeout_handles )
{
clearTimeout( this.timeout_handles[id] )
}
this.timeout_handles[id] = setTimeout( code, time )
}
you can always overwrite the buttons onclick to return false. example:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="UTF-8">
<head>
<title>Javascript example</title>
<script type="text/javascript">
var count = 0;
function annoy() {
document.getElementById('testa').onclick = function() { return false; };
setTimeout(function() {
alert('isn\'t this annoying? ' + count++);
document.getElementById('testa').onclick = window.annoy;
}, 1000);
}
</script>
</head>
<body>
<h2>Javascript example</h2>
Should Only Fire Once<br />
</body>
</html>
You can set a global flag somewhere (like var mouseMoveActive = false;) that tells you whether you are already in a call and if so not start the next one. You set the flag just before you enter the setTimeout call, after checking whether it's already set. Then at the end of the routine called in setTimeout() you can reset the flag.
I'm using this to force a garbage collection on all obsolete timeout references which really un-lagged my script preformance:
var TopObjList = new Array();
function ColorCycle( theId, theIndex, RefPoint ) {
...
...
...
TopObjList.push(setTimeout( function() { ColorCycle( theId, theIndex ,CCr ); },CC_speed));
TO_l = TopObjList.length;
if (TO_l > 8888) {
for (CCl=4777; CCl<TO_l; CCl++) {
clearTimeout(TopObjList.shift());
}
}
}
My original sloppy code was generating a massive array 100,000+ deep over a very short time but this really did the trick!