I am working on a simple higher-order function, delay, that will invoke a function parameter, func, and then delay by an amount of time passed in as wait. I have read other answers about where to put in parameters, but none of the answers I found addressed exactly what I need to learn: where and how should I allow for the parameters that may or may not be passed to func?
Original instructions: "Invokes func after wait milliseconds. Any additional arguments are provided to func when it is invoked."
Here's the basic start:
function delay(func, wait) {
setInterval(func, wait);
}
Another answer on SO states that an anonymous function can be used to wrap the func parameter so that the parameters can be passed in there, but I haven't had any success building that yet.
Guidance is greatly appreciated.
It sounds like you need to make use of the arguments pseudo-array, and Function#apply:
function delay(func, wait) {
// get all arguments after the first two
var args = Array.prototype.slice.call(arguments, 2);
setTimeout(function() {
func.apply(null, args);
}, wait);
}
Example:
function delay(func, wait) {
var args = Array.prototype.slice.call(arguments, 2);
setTimeout(function() {
func.apply(null, args);
}, wait);
}
function outputPerson(firstName, lastName) {
console.log("Hello, " + firstName + " " + lastName);
}
delay(outputPerson, 3000, "John", "Doe");
Edit: As Patrick Evans and I point out in the comments setTimeout already provides the functionality being described here (as long as you're not using IE <10). So you could even define your delay function like this:
var delay = setTimeout;
I think the correct way to model it is to acknowledge delays are only side effects and should not have parameters. The delay should be just the delay and that's it.
You might also want to use promises which are the new(ish) standard language way to perform signular asynchronous actions:
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
See this question and answer about what the new Promise part does and what a promise API looks like.
Then, you can use the delay separately from the function call:
delay(1000).then(fn);
delay(1000).then(() => fn(arg1, arg2, arg3));
And so on.
If you want to keep using callbacks
setTimeout actually already does what you're asking for, setTimeout(fn, 1000) is the easy way to invoke a timeout but you can actually pass additional parameters to the function after the delay amount and the function will be called with them.
May be something link this:
function delay(func, wait,funcArguments) {
setInterval(function() {
funcArguments = funcArguments || [];
func.call(null, funcArguments);
}, wait);
}
funcArguments is array with arguments.
Usually in libraries they name it debounce and you can simply implement it like:
function debounce(func, wait=0,args=[]) {
setTimeout(function() {
func.call({}, args);
}, wait);
}
But a correct implementation, as lodash's has here, is much more complex.
You can just add the parameter for setInterval
var intervalID = window.setInterval(func, delay[, param1, param2, ...]);
function delay(func, wait, param) {
setInterval(func, wait, param);
}
function hello(x) {
var p = document.createElement('p');
p.innerHTML = 'Hello ' + x + '!';
document.body.appendChild(p);
}
delay(hello, 1000, 'world');
This is simple solution:
function delay(func, wait, ...param) {
setTimeout(function(){func(param);}, wait);
}
function doit(a){console.log(a);}
delay(doit,500,'test1','test2');
//['test1','test2']
Related
How can I invoke three times a function with a setTimeOut but just print it once after 100 milliseconds??
This is the definition of debounce that I have to implement:
Debounce ignores the calls made to it during the timer is running and
when the timer expires it calls the function with the last function
call arguments, so I want to achieve that with Javascript
A function will be debounced as follows:
receivedEvent = debounce(receivedEvent, 100)
My attempt:
function debounce(func, timeInterval) {
return (args) => {
setTimeout(func, timeInterval)
}
}
function receivedEvent() {
console.log('receive')
}
receivedEvent();
receivedEvent();
receivedEvent();
But this still generates 3 outputs. I need it to only produce one output according to the requirements.
In your attempt you did not call debounce, but just called your own function receivedEvent. Maybe the site where your attempt is tested will do this for you, but we cannot know this from your question. Just make sure it is called.
To test the requirements you need to use a better use case: one based on a function that receives arguments. This is needed because you must prove that the debounced function is called after the timeout with the last passed arguments.
The key to this pattern is to use variables within a closure:
function debounce(func, timeInterval) {
let timer;
let lastArgs;
return (...args) => {
lastArgs = args; // update so we remember last used args
if (timer) return; // not ready yet to call function...
timer = setTimeout(() => {
func(...lastArgs);
timer = 0; // reset timer (to allow more calls...)
}, timeInterval);
}
}
function receivedEvent(arg) {
console.log('receive ' + arg)
}
receivedEvent = debounce(receivedEvent, 100)
receivedEvent("a");
receivedEvent("b");
receivedEvent("c");
// Output will be "c" after 100ms
Note that the question's definition of "debounce" deviates a bit from its usual definition, where the first invocation actually calls the function immediately, and only then starts the timeout (cooldown-period).
I'm making a shooting game in Javascript: the player will shoot one bullet and will delay for 2 seconds before shooting another one again. I don't know how to make a delay. Using setTimeout, it has a delay, but it will automatically call the function. Can anyone help me?
If I understand the question, you want a system that prevents a function, say shootBullet() from being called until a delay is finished. The simplest way to do this is a global variable and a setTimeout:
var canShoot = true;
function shootBullet() {
if (canShoot) {
//Shoot bullet
}
canShoot = false;
setTimeout(function() {
canShoot = true;
}, 2000);
}
If I'm not misunderstanding, in this case, you need a function which was called "debounce" like this :
function debounce(func, wait) {
let timeout;
return () => { //create closure
let context = this,
args = arguments;
let excuteFunction = () => {
func.apply(context, args)
}
clearInterval(timeout);
timeout = setTimeout(excuteFunction, wait);
};
}
when func is your shooting function and wait is the delay time.
You can read more about the debounce technique at here
Or you can use the built-in debounce function in Lodash: Lodash Docs
so I have a function like this:
function blabla(){
...
setTimeout(() => {
//do some stuff
}, 10000)
}
now How can I reset the time of the timeout (10000) if function was called and timeout was not finished yet?
I tried to kill the timeout if it does exist like this:
function blabla(){
...
if(to){
clearTimeout(to)
}
let to = setTimeout(() => {
//do some stuff
}, 10000)
}
but I get error that to is undefined. so what is the right way to check if a timeout exists or not. is there a better way to do this?
You just need declare to before the if, so that it exists when the if runs (and there is not undefined). You don't have to give it an actual value until later.
Realistically, you probably want to declare it outside the function, so it will persist next time you call the function.
Here's a runnable demo. Notice that despite calling blablah() twice, you only see "hello" once, because the second call to the function cancelled the original timeout.
var to;
function blabla() {
//...
if (to) {
clearTimeout(to)
}
to = setTimeout(() => {
//do some stuff
console.log("hello");
}, 10000)
}
blabla();
blabla();
dont use let, let scope is inside the function block.
if you call the function the second time, the function does not have let to defined.
use var so it is accessible within across function call.
Not good idea use global var for that, because it is not reusable.
Better write wrapper for that function, because it is common pattern. This native code or use npm packet for that
Debounce functions are included in many JavaScript libraries. The goal
behind each implementation is to reduce overhead by preventing a
function from being called several times in succession. Regardless of
the library, all debounce functions are built on JavaScript's native
setTimeout function.
https://www.npmjs.com/package/debounce:
function debounce(func, wait, immediate) {
let timeout;
return function() {
let context = this,
args = arguments;
let later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
var blabla = debounce(function(){
console.log(5)
}, 5000);
blabla()
blabla()
the delay function:Delays a function for the given number of milliseconds, and then calls it with the arguments supplied.
is written from underscore js. annotated source:
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){
return func.apply(null, args);
}, wait);
};
for the delay function to work, why we need to use slice method and call (arguments,2), what does this part do?
Please correct me if im wrong. the delay function first return setTimeout to perform the delay, and the setTimeout function return func.apply(null,args) to pass on all of the information from one function to another? but what is "null" doing here?
when we call a function using delay, it says:
var log = _.bind(console.log, console);
_.delay(log, 1000, 'logged later');
=> 'logged later' // Appears after one second.
im not sure how the optional argument 'logged later' works here since im not sure how bind method works here either? can you please give me a simpler example?
setTimeout executes code in window context, so you have to take care to provide the correct reference for this in the callback function. _.bind does it for you.
var MyObject = function() {
this.myVariable = 'accessible';
this.myMethod = function(message) {
console.log((this === window));
console.log(message + this.myVariable);
}
}
var myObject = new MyObject();
// it will throw an error, because the this.myVariable isn't accessible
_.delay(myObject.myMethod, 1000, 'the correct scope is: ');
// this will be binded to the correct scope
_.delay(_.bind(myObject.myMethod, myObject), 1000, 'the correct scope is: ');
I found a bug, and tracked it down.
You can see a simplified example of my code here.
As it turns out, I need to debounce my if() statement rather than debouncing the function itself.
I'd like to keep the debounce as a standalone function, but I'm not sure then how to pass the conditional in.
Any pointers?
Here's the code:
var foo = function(xyz) {
alert(xyz);
};
function setter(func, arg1, arg2) {
return {
fn: func,
arg1: arg1,
arg2: arg2
};
}
function debounce(someObject) {
var duration = someObject.arg2 || 100;
var timer;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(function() {
someObject.fn(someObject.arg1);
timer = 0;
}, duration);
}
var toggle = true;
if (toggle) {
debounce(setter(foo, 'The best things in life are worth waiting for.', 1250));
} else {
foo('Instant gratification is sweet!!');
}
Using your example, why not pass toggle in as arg 1... something like:
var toggle = true;
var debouncedFunk = function(toggle) {
if (toggle)
// the function call
else
// something else
};
debounce(debouncedFunk, toggle, 1250);
You should also look into using the Function objects .call and .apply methods. They are for calling the function and passing in arguments. Taking the example function:
var example = function(one, two) {
// Logic here
};
You can call it in three ways:
// First
example(1, 2);
// Second
example.call({}, 1, 2);
// Third
example.apply({}, [ 1, 2 ]);
The first is the standard way to call a function. The difference between the first and the .call is that the first parameter to .call is the context object of the function (what this will point to inside the function), the other parameters are passed after that (and a known list is required for .call. The benefit of .apply is that you can pass an array to the function of arguments and they will be assigned to the parameter list appropriately, the first parameter is still the context object.
It would simplify your debounce function, instead of having to deal with a structured object as you currently do.
A suggestion for your debounce:
var debounce = function(funk, delay) {
var args = [];
if (arguments.length > 2)
args = [].slice.call(arguments, 2);
setTimeout(function() { funk.apply({}, args); }, delay);
};
Changing your current if to:
var toggle = true;
var debouncedFunk = function(toggle) {
if (toggle)
// Do if true
else
// DO if false
};
debounce(debouncedFunk, 1000, toggle);
Maybe too much information (sorry)?
As a last note, I'd recommend using a framework (if possible) where these functions have been implemented already (and many other useful functions) such as Underscore. Using Underscore your example would look like:
// Define debouncedFunk and toggle
debouncedFunk = _.bind(debouncedFunk, {}, toggle);
debouncedFunk = _.debounce(debouncedFunk, 1000);
debouncedFunk();
EDIT
Fixed the underscore example, _.debounce returns a function that will execute only after the delay but it still needs to be called.