Blocking "wait" function in javascript? - javascript

As part of a Javascript project I'm working on, there are some synchronous ajax calls (I guess that makes it "sjax", but I digress). I'm now writing a debugging panel which would allow me to test out the site with some artificially simulated network conditions by wrapping $.ajax. Simple things: faking a 500 response etc, and making the ajax calls take much longer.
For the asynchronous calls, it's simple. When the real response comes back, add a setTimeout to make it wait for the artificial response time before triggering the callback. However, this doesn't work with the synchronous calls obviously, since setTimeout isn't synchronous.
So, is there a way to make a Javascript program perform a blocking wait for a set amount of time?
The only thing I could think of would be something like this:
function wait(ms) {
var start = +(new Date());
while (new Date() - start < ms);
}
Is there a better solution?
(Also, please assume there's a good reason for the blocking ajax calls... :-\)

Do not do it on the JavaScript level. Get a proxy such as Fiddler and set up an AutoResponder to delay the call by a time period.

If it's just for debugging purposes to have an artificial delay:
alert('block me one more time');
There is no reasonable other approach to have a blocking code in ECMAscript. Since Javascript is executed in the same thread ("UI thread") which browsers use to render the DOM and to certain other things, the whole show was designed not to block anything.
Of course you can fake it by using a loop, but its a perversion of the show.

I figured this code might help
// execute code consecutively with delays (blocking/non-blocking internally)
function timed_functions()
{
this.myfuncs = [];
this.myfuncs_delays = []; // mirrors keys of myfuncs -- values stored are custom delays, or -1 for use default
this.myfuncs_count = 0; // increment by 1 whenever we add a function
this.myfuncs_prev = -1; // previous index in array
this.myfuncs_cur = 0; // current index in array
this.myfuncs_next = 0; // next index in array
this.delay_cur = 0; // current delay in ms
this.delay_default = 0; // default delay in ms
this.loop = false; // will this object continue to execute when at end of myfuncs array?
this.finished = false; // are we there yet?
this.blocking = true; // wait till code completes before firing timer?
this.destroy = false; // <advanced> destroy self when finished
// handle next cycle execution
this.next_cycle = function() {
var that = this;
var mytimer = this.delay_default;
if(this.myfuncs_cur > -1)
if(this.myfuncs_delays[this.myfuncs_cur] > -1)
mytimer = this.myfuncs_delays[this.myfuncs_cur];
console.log("fnc:" + this.myfuncs_cur);
console.log("timer:" + mytimer);
console.log("custom delay:" + this.myfuncs_delays[this.myfuncs_cur]);
setTimeout(function() {
// times up! next cycle...
that.cycle();
}, mytimer);
}
this.cycle = function() {
// now check how far we are along our queue.. is this the last function?
if(this.myfuncs_next + 1 > this.myfuncs_count)
{
if(this.loop)
{
console.log('looping..');
this.myfuncs_next = 0;
}
else
this.finished = true;
}
// first check if object isn't finished
if(this.finished)
return false;
// HANDLE NON BLOCKING //
if(this.blocking != true) // blocking disabled
{
console.log("NOT BLOCKING");
this.next_cycle();
}
// set prev = current, and current to next, and next to new next
this.myfuncs_prev = this.myfuncs_cur;
this.myfuncs_cur = this.myfuncs_next;
this.myfuncs_next++;
// execute current slot
this.myfuncs[this.myfuncs_cur]();
// HANDLE BLOCKING
if(this.blocking == true) // blocking enabled
{
console.log("BLOCKING");
this.next_cycle();
}
return true;
}; // END :: this.cycle
// adders
this.add = {
that:this,
fnc: function(aFunction) {
// add to the function array
var cur_key = this.that.myfuncs_count++;
this.that.myfuncs[cur_key] = aFunction;
// add to the delay reference array
this.that.myfuncs_delays[cur_key] = -1;
}
}; // end::this.add
// setters
this.set = {
that:this,
delay: function(ms) {
var cur_key = this.that.myfuncs_count - 1;
// this will handle the custom delay array this.that.myfunc_delays
// add a custom delay to your function container
console.log("setting custom delay. key: "+ cur_key + " msecs: " + ms);
if(cur_key > -1)
{
this.that.myfuncs_delays[cur_key] = ms;
}
// so now we create an entry on the delay variable
}, // end :: this.set.delay(ms)
delay_cur: function(ms) { this.that.delay_cur = ms; },
delay_default: function(ms) { this.that.delay_default = ms; },
loop_on: function() { this.that.loop = true; },
loop_off: function() { this.that.loop = false; },
blocking_on: function() { this.that.blocking = true; },
blocking_off: function() { this.that.blocking = false; },
finished: function(aBool) { this.that.finished = true; }
}; // end::this.set
// getters
this.get = {
that:this,
delay_default: function() { return this.that.delay_default; },
delay_cur: function() { return this.that.delay_cur; }
}; // end::this.get
} // end ::: timed_functions()
And Test...
// // // BEGIN :: TEST // // //
// initialize
var fncTimer = new timed_functions;
// set some defaults
fncTimer.set.delay_default(1000); // set a default delay between function blocks
fncTimer.set.blocking_on(); // next timer begins count before code is executed
fncTimer.set.blocking_off(); // next timer begins count after code is executed
// fncTimer.set.loop_on(); // when finished start over
// fncTimer.set.loop_off();
// BEGIN :: ADD FUNCTIONS (they will fire off in order)
fncTimer.add.fnc(function() {
console.log('plan a (2 secs)');
});
fncTimer.set.delay(2000); // set custom delay for previously added function
fncTimer.add.fnc(function() {
console.log('hello world (delay 3 seconds)');
});
fncTimer.set.delay(3000);
fncTimer.add.fnc(function() {
console.log('wait 4 seconds...');
});
fncTimer.set.delay(4000);
// END :: ADD FUNCTIONS
// NOW RUN
fncTimer.cycle(); // begin execution
// // // END :: TEST // // //

Related

How to implement a throttle-like function but without loosing time between calls

I would like to implement a helper function like lodash's throttle, but which calls the passed callback only if the last call ended (and if not, delays the new call until then), instead of having a rule like "one call every x milliseconds".
What's the best approach here? Thanks.
So What you eventually want is to queue your function calls and call them from the stack.
// keep a stack of inputs for the function calls
var stack = [],
timer = null;
function process() {
var item = stack.shift();
//
// process the item here
//
if (stack.length === 0) {
clearInterval(timer);
timer = null;
}
}
// every time you call the function on event. instead of calling the processing function
// call this to add it in a queue
function queue(item) {
stack.push(item);
if (timer === null) {
timer = setInterval(process, 500);
}
}
You can make this function work for multiple types of calls too.
// use item as a deciding factor
// keep a stack of inputs for the function calls
var stack = [],
timer = null;
function reset(item){
// reset item here
}
function process() {
var item = stack.shift();
//
// process the item here
//
switch (item.call) {
case 'reset':
reset(item);
break;
case 'stop':
// stop(item);
break;
default:
// code block
// deal with it
}
if (stack.length === 0) {
clearInterval(timer);
timer = null;
}
}
// every time you call the function on event. instead of calling the processing function
// call this to add it in a queue
// for hybrid calls
// use item = {'call': 'reset', 'data': item}
function queue(item) {
stack.push(item);
if (timer === null) {
timer = setInterval(process, 500);
}
}
try using boolean variable like
var running = false;
use setInterval or similar function and put condition if(!running){} for statements to be performed. if statement is going to run then make running = true.
Finally worked out a way :
class Scheduler {
private readonly stack: Function[] = [];
private enqueue(task: Function) {
if (this.stack.length < 2) this.stack.push(task);
else this.stack[1] = task;
}
private dequeue() {
if (this.stack.length) {
const task = this.stack.shift()!;
task();
defer(() => this.dequeue());
}
}
run(task: Function) {
this.enqueue(task);
defer(() => this.dequeue());
}
}
// Usage :
const scheduler = new Scheduler()
// Very frequent call :
scheduler.run(() => { /* your heavy task */ })

Function queue based on time

I want implement function queue,where it should be executed based upon passed in time delay. If any of the function in the queue is invoked before the delay, then queue must not execute that function and should be moved to the next function in the queue.
For example:
function funQueue(message,date){
//logic
}
var fn1=funQueue("message1",new Date().getTime()+500)
var fn2=funQueue("message2",new Date().getTime()+1000)
var fn3=funQueue("message3",new Date().getTime()+2000)
fn2()
When it is executed like the above it should print.
message1
message3
please note message2 is not printed.
Thanks all,
Maybe a little something like the following, using setTimeout() and clearTimeout():
function funQueue(message,date){
var timeoutId = setTimeout(function() { // schedule a timeout
console.log(message) // that logs the message
}, date - new Date().getTime()) // at the specified time
return function() { // return a function
clearTimeout(timeoutId) // that cancels the timeout
}
}
var fn1=funQueue("message1",new Date().getTime()+500)
var fn2=funQueue("message2",new Date().getTime()+1000)
var fn3=funQueue("message3",new Date().getTime()+2000)
fn2()
Note that if you call funQueue() with a time that is in the past it will still call setTimeout(), which means the message would be logged immediately after the current code completes unless it is cancelled within the current code.
EDIT: I was asked in a comment to implement it without multiple simultaneous timeouts. The following is the first way that came to mind. I'm sure this could be tidied up, optimised, and/or completely rewritten in a better way, but it worked the first time I clicked "run" so I think it's a good enough starting place. I'll leave the explanation of how it works as an exercise for the reader...
var funQueue = function() {
var queue = []
var timeoutId
function setNext() {
clearTimeout(timeoutId)
if (queue.length > 0) {
timeoutId = setTimeout(function() {
console.log(queue.shift().message)
setNext()
}, queue[0].date - new Date().getTime())
}
}
return function(message,date){
var item = { message: message, date: date }
var i = 0
while (i < queue.length && date >= queue[i].date) i++
queue.splice(i, 0, item)
setNext()
return function() {
var i = queue.indexOf(item)
if (i != -1) {
queue.splice(i, 1)
setNext()
}
}
}
}()
var fn1=funQueue("message1",new Date().getTime()+500)
var fn2=funQueue("message2",new Date().getTime()+1000)
var fn3=funQueue("message3",new Date().getTime()+2000)
var fn4=funQueue("message4",new Date().getTime()+1500)
var fn5=funQueue("message5",new Date().getTime()+2000)
fn2()
Do something like this to check whether the time has passed or not
var timePassed;
function funQueue(message, date){
if(timePassed > date){
timePassed = new Date.getTime();
// Do stuff
}
}

html - Setting a timer in javascript [duplicate]

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);
}

Coordinating Asynchronous Requests in Javascript

I want to get two resources using two asynch calls. I want to proceed only when both resources have been retrieved.
How can I do this elegantly in JS?
This would work:
getStuff1(function (result1) {
getStuff2 (function (result2) {
// do stuff with result1 and result2
....
}
}
but stuff2 only starts after stuff1 completes. I'd prefer to start stuff2 while waiting on stuff1.
If you know that functions are in fact first-class objects in Javascript, you can come up with a fairly elegant solution.
Without any extra objects, or global variables.
function callback1() {
callback1.done = true;
commonCallback();
}
function callback2() {
callback2.done = true;
commonCallback();
}
function commonCallback() {
if (callback1.done && callback2.done) {
// do stuff, since you know both calls have come back.
}
}
Why is this so elegant? Because you've encapsulated the data, your scope is free from useless variables and the code is more readable than ever. How cool is that? :)
UPDATE
And if you want a bit more general solution you may try the following:
function callback() {
callback.number -= 1;
if (callback.number === 0) {
// do stuff since all calls finished
callback.last();
}
}
callback.newQueue = function(num, last) {
callback.number = num;
callback.last = last;
}
// EXAMPLE USAGE
// our last function to be invoked
function afterEverythingDone(){ alert("done"); }
// create a new callback queue
callback.newQueue(3, afterEverythingDone);
// as time passes you call the callback
// function after every request
callback();
callback();
callback();
// after all call is finished
// afterEverythingDone() executes
Awesomeness again :)
One way is to use the same callback for both requests and proceed when both are complete:
var requestStatus = {
fooComplete: false,
barComplete: false
};
function callback(data) {
if (isFoo(data)) {
requestStatus.fooComplete = true;
} else if (isBar(data)) {
requestStatus.barComplete = true;
}
if (requestStatus.fooComplete && requestStatus.barComplete) {
proceed();
}
}
getAsync("foo", callback);
getAsync("bar", callback);
You'll probably want to flesh this out into a class.
Edit: added the async calls for clarity
You could have the callback function for each one indicate that their respective request has come back, and then execute the same common function. To illustrate:
var call1isBack = false;
var call2isBack = false;
function call1Callback() {
call1isBack = true;
commonCallback();
}
function call2Callback() {
call2isBack = true;
commonCallback();
}
function commonCallback() {
if (call1isBack && call2isBack) {
// do stuff, since you know both calls have come back.
}
}
Use a common callback handler with a counter that only allows passage into the "actual" processing section after the counter meets or exceeds the number of pending requests:
var commonHandler = (function() {
var counter=0, pendingCalls=2;
return function() {
if (++counter >= pendingCalls) {
// Do the actual thing...
}
}
})();
makeAjaxCall({args:args1, onComplete:commonHandler});
makeAjaxCall({args:args2, onComplete:commonHandler});
Using a closure around the anonymous function lets you avoid using a global variable for the counter.
Here's a snippet from a concurrent library I'm working on. All you need to do is instantiate a new Concurrent.Counter with the number of requests to await (before you execute them), and the callback to execute when they have finished. Before each of the asynchronous functions returns, have it call the decrement() method of the counter; once the counter has been decremented the number of times specified, the callback will be executed:
// Ensure the existence of the "Concurrent" namespace
var Concurrent = Concurrent || {};
/**
* Constructs a new Concurrent.Counter which executes a callback once a given number of threads have
* returned. Each Concurrent.Counter instance is designed to be used only once, and then disposed of,
* so a new one should be instantiated each additional time one is needed.
*
* #param {function} callback The callback to execute once all the threads have returned
* #param {number} count The number of threads to await termination before executing the callback
*/
Concurrent.Counter = function(callback, count) {
/**
* Decrements the thread count, and executes the callback once the count reaches zero.
*/
this.decrement = function() {
if (!(-- count)) {
callback();
}
};
};
// The number of asynchronous requests to execute
var requests = 10,
// Executes a callback once all the request tasks have returned
counter = new Concurrent.Counter(function() {
// this will be executed once the tasks have completed
}, requests),
// Tracks the number of requests made
i;
for (i = 0; i < requests; i ++) {
setTimeout(function() {
/*
* Perform an asynchronous task
*/
// Decrement the counter
counter.decrement();
}, 0);
}
This is written off the top of my head, but it should work.
function createCoordinator(onFinish) {
var count = 0;
return function (callback) {
count++;
return function () {
if (callback.apply(this, arguments))
count--;
if (count == 0)
onFinish();
}
}
}
var coordinate = createCoordinator(function () { alert('done!') });
// Assume sendAJAX = function (url, onreadystatechange)
sendAJAX('url1', coordinate(function () {
if (this.readyState != 4)
return false; // Return false if not done
alert('Done with url1!');
return true;
}));
sendAJAX('url2', coordinate(function () {
if (this.readyState != 4)
return false; // Return false if not done
alert('Done with url2!');
return true;
}));

JavaScript getTimeout?

The window.setTimeout (and related setInterval) function in Javascript allows you to schedule a function to be executed sometime in the future:
id = setTimeout(function, delay);
where "delay" is the number of milliseconds into the future at which you want to have the function called. Before this time elapses, you can cancel the timer using:
clearTimeout(id);
What I want is to update the timer. I want to be able to advance or retard a timer so that the function gets called x milliseconds sooner or later than originally scheduled.
If there were a getTimeout method, you could do something like:
originally_scheduled_time = getTimeout(id);
updateTimeout(id, originally_schedule_time + new_delay); // change the time
but as far as I can tell there's nothing like getTimeout or any way to update an existing timer.
Is there a way to access the list of scheduled alarms and modify them?
Is there a better approach?
thanks!
If you really want this sort of functionality, you're going to need to write it yourself.
You could create a wrapper for the setTimeout call, that will return an object you can use to "postpone" the timer:
function setAdvancedTimer(f, delay) {
var obj = {
firetime: delay + (+new Date()), // the extra + turns the date into an int
called: false,
canceled: false,
callback: f
};
// this function will set obj.called, and then call the function whenever
// the timeout eventually fires.
var callfunc = function() { obj.called = true; f(); };
// calling .extend(1000) will add 1000ms to the time and reset the timeout.
// also, calling .extend(-1000) will remove 1000ms, setting timer to 0ms if needed
obj.extend = function(ms) {
// break early if it already fired
if (obj.called || obj.canceled) return false;
// clear old timer, calculate new timer
clearTimeout(obj.timeout);
obj.firetime += ms;
var newDelay = obj.firetime - new Date(); // figure out new ms
if (newDelay < 0) newDelay = 0;
obj.timeout = setTimeout(callfunc, newDelay);
return obj;
};
// Cancel the timer...
obj.cancel = function() {
obj.canceled = true;
clearTimeout(obj.timeout);
};
// call the initial timer...
obj.timeout = setTimeout(callfunc, delay);
// return our object with the helper functions....
return obj;
}
var d = +new Date();
var timer = setAdvancedTimer(function() { alert('test'+ (+new Date() - d)); }, 1000);
timer.extend(1000);
// should alert about 2000ms later
I believe not. A better approach might be to write your own wrapper which stores your timers (func-ref, delay, and timestamp). That way you can pretend to update a timer by clearing it and calculate a copy with an updated delay.
Another wrapper:
function SpecialTimeout(fn, ms) {
this.ms = ms;
this.fn = fn;
this.timer = null;
this.init();
}
SpecialTimeout.prototype.init = function() {
this.cancel();
this.timer = setTimeout(this.fn, this.ms);
return this;
};
SpecialTimeout.prototype.change = function(ms) {
this.ms += ms;
this.init();
return this;
};
SpecialTimeout.prototype.cancel = function() {
if ( this.timer !== null ) {
clearTimeout(this.timer);
this.timer = null;
}
return this;
};
Usage:
var myTimer = new SpecialTimeout(function(){/*...*/}, 10000);
myTimer.change(-5000); // Retard by five seconds
myTimer.change(5000); // Extend by five seconds
myTimer.cancel(); // Cancel
myTimer.init(); // Restart
myTimer.change(1000).init(); // Chain!
It may be not exactly what you want, but take a look anyway, maybe you can use it to your benefit.
There is a great solution written by my ex-coworker that can create special handler functions that can stop and start timeouts when required. It is most widely used when you need to create a small delay for hover events. Like when you want to hide a mouseover menu not exactly at the time when a mouse leaves it, but a few milliseconds later. But if a mouse comes back, you need to cancel the timeout.
The solution is a function called getDelayedHandlers. For example you have a function that shows and hides a menu
function handleMenu(show) {
if (show) {
// This part shows the menu
} else {
// This part hides the menu
}
}
You can then create delayed handlers for it by doing so:
var handlers = handleMenu.getDelayedHandlers({
in: 200, // 'in' timeout
out: 300, // 'out' timeout
});
handlers becomes an object that contains two handler functions that when being called cancel the other one's timeout.
var element = $('menu_element');
element.observe('mouseover', handlers.over);
element.observe('mouseout', handlers.out);
P.S. For this solution to work you need to extend the Function object with the curry function, which is automatically done in Prototype.
One possibility can be like this:
if (this condition true)
{
setTimeout(function, 5000);
}
elseif (this condition true)
{
setTimeout(function, 10000);
}
else
{
setTimeout(function, 1000);
}
It's up to your how you construct your conditions or the logic. thanks

Categories

Resources