JS: What are the args in a debounce function - javascript

I am trying to grasp a debounce function in an existing codebase I am working on but I have a question that I can't understand while looking at it. What is (...args) in this scenario? I know it's a rest parameter but where did it come from and what is the purpose of args here? Why can't that just be empty and then called like fn() in the setTimout?
const debounce = (fn, delay) => {
let id;
return function(...args) {
if (id) {
clearTimeout(id)
}
id = setTimeout(() => {
fn(...args);
}, delay);
}
}

The function being returned from debounce() is supposed to act exactly the same as the function being provided, except for the fact that we're limiting how often it gets called. This means that if the original function was supposed to take two parameters, the returned function should too. That's why spread is being used.
As a simpler example, lets consider a wrapper function that just adds 1 to whatever gets returned.
function alsoAdd1(fn) {
return function(...args) {
return fn(...args) + 1
}
}
To use it, we can do the following:
> multiplyAndAdd1 = alsoAdd1(function(x, y) { return x * y })
> multiplyAndAdd1(2, 3)
7
> doubleAndAdd1 = alsoAdd1(function(x) { return x * 2 })
> doubleAndAdd1(2)
5
Notice how it doesn't matter how many parameters we pass into the wrapped function (like doubleAndAdd1), they're all get passed along to the original function (the double function). This is because we're using the spread operator.
If we look at the definition of alsoAdd1, we see that it'll give back a function that takes an arbitrary number of arguments, and passes all the arguments it receives into the function its wrapping.
The debounce() function is doing exactly the same thing, there's just a little bit more going on. These wrapper functions can be a little tricky to wrap your head around, so try going through those above examples slowly and see if you can grasp what they're doing.

Related

Attempting to pass a string to a constructed function

still floundering around in my attempts to understand functions. How would I go about constructing a function with values I want to pass to it?
var box = $('#box1');
function pushCard(arg1) {
if (this.style.opacity == 0.5) {
this.style.opacity = 1;
}
else {
this.style.opacity = 0.5;
window.alert(arg1);
}
}
box.click(pushCard('String'));
tl;dr: be aware of the difference between functions / function results and when functions are passed as values / when they're called (and their result is passed)
The culprit is with this line:
box.click(pushCard('String'));
You're calling box.click() with "something". JavaScript needs to evaluate expressions before passing them as function arguments.
In this case, you instruct JavaScrip to run box.click(pushCard('String')) = call box.click with value of pushCard('String') as first parameter.
In order to do this, JavaScript first needs to evaluate pushCard('String') by running pushCard with value 'String' as first parameter (this doesn't need more evaluation since it's already a value).
The result of pushCard('String') is undefined (you're returning nothing from that function). So in effect, it's the equivalent of box.click(undefined).
This is what you want:
box.click(function() {pushCard('String')});
(or with ES6 arrow functions: box.click(() => pushCard('String'));)
In this case, you're assigning box.click() a function. This is what jQuery .click()` expects, a click handler which will run (be evaluated) then the click happens, not when the click handler is assigned.
In JavaScript, you can pass functions as values, and they are evaluated when explicitly called:
function test() {
alert('I have been called');
}
function delay_it(handler) {
setTimeout(function() {
handler(); // ⇽ call given function
}, 1000);
// shorter: setTimeout(handler, 1000);
}
// Wrong (we're calling test() by ourselves and passing the result (undefined) to delay_it
delay_it(test());
// Correct (we're giving test as function to delay_it, delay_it will call the function)
delay_it(test);

Javascript - why my function is recursive?

I don't know where am I going wrong. Why is my function resursive? I expect a result of sum = 3;
function init() {
let object = {
firsNum: 1,
sum: 0,
};
add(1, object.firsNum, fun);
console.log(object.sum);
}
function fun(a) {
a.firstNum++;
a.sum += a.firsNum;
fun(a);
}
function add(a, b, callback) {
return callback(a + b);
}
init();
I think you don't fully understand every programming concept you've used in your code.
Apart from that your original code contains a typo: a.firstNum++; where it should read firsNum like in all other places.
Let's break down your code into separate concerns.
Recursion
Recursion means that a function calls itself. Recursion is one way to compute, iterate or traverse data. The other one is iteration using loops like while or for.
Let's look at this simplified example:
function recurse(counter) {
console.log(counter);
recurse(counter + 1);
}
recurse(0);
This will call recurse() indefinitely since there's nothing that stops it to call itself. Recursion as well as iteration require some stop condition to break the recursion.
If you'd like to stop at a certain value you'd have to call the function only until that condition is met:
function recurse(counter) {
console.log(counter);
if (counter < 42) {
recurse(counter + 1);
}
}
recurse(0);
This will only recurse and increment until 42 is reached.
You could even pass the stop condition with the function itself:
function recurse(counter, maxValue) {
console.log(counter);
if (counter < maxValue) {
recurse(counter + 1, maxValue); // maxValue gets passed along
}
}
recurse(0, 42);
To make your recursive function stop at a certain value you've to add such a condition:
function fun(a) {
a.firstNum++;
a.sum += a.firstNum;
if (a.sum < a.maxNum) {
fun(a);
}
}
You'd then have to ensure your object specifies the condition:
let object = {
maxNum: 3, // stop condition added
firsNum: 1,
sum: 0,
};
Though this on its own won't solve the problem. Read on.
Callbacks
Callbacks are functions passed as function parameters, object properties or array elements to other functions.
Callbacks allow you to decide what function to call during the runtime without having to implement branching behavior using if or switch statements and requiring certain function names at time of writing.
function someCallback() { /* ... */ }
function callTheCallback(callback) {
callback(); // Execute the parameter as a function
}
callTheCallback(someCallback);
let someCallbackReference = someCallback;
// Call the same function but indirectly via a variable
callTheCallback(someCallbackReference);
In the above example someCallback could indirectly origin from an other object.
You could even directly specify a function expression as a callback:
callTheCallback(function() {
// ...
});
With callbacks it's crucial to grasp the difference between passing a function call result and the function itself:
callTheCallback(someCallback); // Callback function passed
callTheCallback(someCallback()); // Result of a function call passed
Note that it could be perfectly valid to pass the result of a function call if the return value itself is a function. This is also not uncommon in programming.
Callbacks are often used for inversion of control (IoC) in general and asynchronous programming specifically.
Examples for callback functions:
Filtering arrays: Array.prototype.filter() (IoC synchronous)
Events: window.onload (asynchronous)
In your case fun() is passed as third parameter callback to add().
Now that you know how to use recursion and callbacks there are still semantic errors in your code...
Semantical errors
In contrast to syntactical errors (wrong code grammar) semantical errors are errors with the meaning of the code.
Interface of add()
Analyze your function API:
function add(a, b, callback) {
return /* some value - callback() call removed for simplicity */;
}
add() accepts three parameters and returns a value. The third parameter is named "callback" and thus shall be a function.
Your call of add() looks sensible although you don't use the returned value (which is not necessarily an error).
add(1, object.firsNum, fun);
Implementation of add()
Since the parameters of add() are not documented, it is ambiguous what your intention was.
The name along with the fact that there are two parameters, a and b, leads one to the assumption that the two values shall be added. And you very well do this:
return callback(a + b);
But! what you've passed as callback function - which is fun() - expects different parameters, not a number. fun() expects an object.
Properly documented it would look like this:
/**
* Recursively sum values until a maximum value is reached.
*
* #param {Object} a object containing the sum, the increment and the max value
* #param {Number} a.firsNum start value
* #param {Number} a.maxNum the maxium value up to which to add "firsNum" to "sum"
* #param {Number} a.sum the summed value, shall be 0 initially
* #returns {undefined} nothing is returned
*/
function fun(a) {
// ...
}
The above comment style is called JsDoc: https://jsdoc.app
Note to use proper names for functions, variables and parameters. Proper names help documenting the code whithout writing explicit documentation comments.
Fixing the error
With the limited knowledge on what your actually wanted to achive one can only speculate. I assume add(), although it's name and parameters looks sensible, is implemented wrong. Your issue could be solved by calling the callback (fun()) correctly:
function add(obj, callback) {
return callback(obj);
}
And:
add(object, fun);
Since we require the object add() now expects it as a single parameter.
Well that does not make a lot sense anymore now. fun() could be called directly from within init() and not via a callback reference and add() has as a confusing name.
I won't further speculate what your intention was and how to implement alternative solutions to the problem.
What you've learned
About Mozilla developer network
How recursion works
Functions can be passed as parameters and be assigned to variables or properties
Reading code
The benefits of documenting code and properly naming functions and variables
Just commented out the recursive code
function init() {
let object = {
firsNum: 1,
sum: 0,
};
add(1, object.firsNum, fun);
console.log(object.sum);
}
function fun(a) {
a.firstNum++;
a.sum += a.firsNum;
//fun(a);
}
function add(a, b, callback) {
return callback(a + b);
}
init();
Your code
function init() {
let object = {
firsNum: 1,
sum: 0,
};
add(1, object.firsNum, fun);
console.log(object.sum);
}
function fun(a) {
a.firstNum++;
a.sum += a.firsNum;
fun(a);
}
function add(a, b, callback) {
return callback(a + b);
}
init();
init
Defines an object with attributes of firsNum and sum, then calls add, passing 1, 1 and fun.
fun
Receives a parameter. Increments firstNum of the parameter and adds it to sum. Then it calls itself.
add
Gets three parameters: a, b and callback. Calls callback and passes a + b to it.
The flow
init is called, then it calls add and passes 1 to a, 1 to b and fun to callback. As a result, add calls callback, which is fun and since both a and b are 1, a + b = 2 is passed to fun. fun increments a.firstNum, which seems to be a bug, since a is a number, then some other strange value is added to sum. Just remove fun(a); inside fun to get rid of recursion, but that's not the only bug in your code. Also, make sure that there is a return statement inside fun.

Passing function as parameter on javascript

I recently started to learn javascript and saw this code. It was very different than C++ so please tell me what it means.
function getWeather(lat, lng){
fetch(
`https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lng}&appid=${API_KEY}&units=metric`
)
.then(function(response){
return response.json();
})
.then(function(json){
const temp = json.main.temp;
const place = json.name;
weather.innerText = `${temp} # ${place}`;
});
}
so basically it gets api and fetch it. After that it continues with .then(function(response)
I have no idea where that response come from. I can guess that response part is something returned from fetch function but still I don't understand how that happens.
const parsedToDos = JSON.parse(loadedToDos);
//for each takes function and execute function on each items.
parsedToDos.forEach(function(toDo){
paintToDo(toDo.text);
});
similar to the previous example, it takes toDo as parameter and it is not even global or local variable in this code but still used. Can anyone tell me the logic behind this?
Which arguments a function receivedsis determined by the caller of the function. If you have the following function
function add(a, b) {
return a + b;
}
Then "you" can call it as
add(1, 3)
Sometimes it is not directly "you" who calls the function, but another function. E.g.
function add1(a) {
return add(a, 1);
}
here add is called by add1. add1 will always pass 1 as second argument.
In addition to that, sometimes functions accept other functions as arguments and call them at some point. Again an example:
function doSomething(callback) {
return callback(40, 2);
}
Here doSomething accepts a function as argument and will always pass 40 and 2 as arguments. If I pass a function that expects to be passed two numbers it will work as expected:
// Works
doSomething(function (a, b) { return a + b; });
If I don't not, then I get some unexpected behavior or an error:
// Error :(
doSomething(function (a, b) { a.foo.bar = b });
But if that happens then I haven't read the documentation properly, because functions which accept callbacks usually explain which arguments are passed to the callback. For example for forEach (emphasis mine):
forEach() calls a provided callback function once for each element in an array in ascending order. It is not invoked for index properties that have been deleted or are uninitialized (i.e. on sparse arrays, see example below).
callback is invoked with three arguments:
the value of the element
the index of the element
the Array object being traversed
Similar for .then.
In summary, arguments are provided by the caller. If you pass a callback to a function then that function will most likely be caller and pass arguments. Read the documentation or look at the source code to find out which arguments these are.

How to properly use JavaScript callback without jquery

I have been using jquery and javascript for a long time. I have rarely ever written javascript without jquery, and this instance requires me understand callbacks with javascript. Its a simple case, but I don't understand how it works. Not only do I want to get it working, I want to understand the callback.
const ALL_DATES = ["05/01/1992", "01/01/2017", "09/17/2010", "07/07/2017", "07/17/2017", "09/23/2013", "03/30/2012";
//console.log(ALL_DATES);
function filterDates(input) {
// Do your filtering here and use ALL_Dates
return input;
}
export function getDates(input, cb) {
setTimeout(() => {
cb(filterDates(input));
}, 300);
};
First I create fake data in the constant: ALL_DATES. filterDates function will be used to do filtering to my specific criteria.
What I don't fully grasp is the usage of getDates function (in the case, this function has to be utilized) and what I will be using for the callback function. In jquery, for example, what I would do is use the .change to check for a change event on an input, and run the getDates function again.
Can someone please help me grasp and understand the usage of the callback here, and what purpose it has to serve?
Thank you!
"What I don't fully grasp is the usage of getDates function (in the case, this function has to be utilized) and what I will be using for the callback function..."
The callback function will be whatever desires to receive the filtered dates. So whatever code calls getDates will presumably want to do something with the dates, and so will pass a callback that expects to receive those dates.
// Userland code
getDates(function(dates) {
// User now gets to work with the list of dates that you filtered internally
dates.forEach(d => console.log("The date is %s", d));
})
However, if there's nothing asynchronous going on, you could just have getDates() return the dates instead of receiving a callback function. In your case, the setTimeout provides async behavior, but barring any sort of async processing like that, you'd more likely just return them.
If you take out the node and the jQuery - and try and build a simplified version - you may have a quicker time to grok it. Maybe these will help. A callback is just a function passed to another function. 'higher order function' is a confusing way of marketing it. jQuery methods often have an optional 'callback' /anonymous function that can run AFTER whatever that method does is done. The syntax is something you get used to - but it fogs up the view... if you aren't aware of the methods arguments and true nature.
$(document).ready( function() {
console.log('DOM ready');
});
// vs
function sayReady() {
console.log('DOM ready again...');
}
$(document).ready(sayReady);
examples: https://jsfiddle.net/sheriffderek/b6jj5z2u/
For the most part now though... async stuff is much easier to reason about with promises or async/await techniques... say you get data from an API, and you don't want to try and 'do' anything with it until it is all there / callbacks are pretty tough to write all the logic for - comparatively.
console.clear();
var exampleArray = [2, 45, 63, 7, 9, 12];
function doubleInput(number) {
return number * 2;
}
function iterateOverArray(array) {
console.log('iterateOverArray');
for (var i = 0; i < array.length; i++) {
console.log('-', array[i] );
}
return true;
// you should really be returning something...
}
iterateOverArray(exampleArray);
// this function accepts an array and a function to run on each item
// not far off from array.forEach()...
function iterateAndRunFunction(array, exampleCallback) {
console.log('iterateAndRunFunction');
var newArray = [];
for (var i = 0; i < array.length; i++ ) {
var manipulatedValue = exampleCallback( array[i] );
newArray.push(manipulatedValue);
}
console.log(newArray);
return newArray;
// you should really be returning something...
}
iterateAndRunFunction(exampleArray, doubleInput);
function saySomething(message) {
console.log(message);
}
function doSomethingLater(functionToRun, waitTime) {
console.log('wait ' + waitTime + 'ms');
setTimeout( function() {
saySomething('HI!!! sorry I took ' + waitTime + 'ms');
}, waitTime);
}
doSomethingLater(saySomething, 3000);
A callback is a reference to a function (function A) that you hand over to another function (function B) so that function B can call function A when it decides to. Check this out:
const FIVE_MINS = 300000
const rightNow = new Date()
function getTime(startTime) {
if (typeof startTime === 'undefined') startTime = new Date()
alert(startTime.getHours() + ":" + startTime.getMinutes() + ":" + startTime.getSeconds())
}
getTime() is a function that alerts the user to the time that is passed to it as an argument, or if it receives no argument, the time at the moment it is called.
setTimeout() wants a function as its first argument so it can call that function in 5 minutes. If you were to do this:
setTimeout(getTime(), FIVE_MINS)
you would see the alert immediately because getTime() IS BEING CALLED in this expression. Don't do that. setTimeout() is expecting a function reference so that it can call getTime() itself when the time comes. There is two ways to reference a function.
If the function needs no arguments, you can just pass the name without the parens. That is a reference to a function and the function can be dealt with later.
setTimeout(getTime, FIVE_MINS)
Notice getTime() doesn't have an argument, so it is going to use whatever "now" is at the time setTimeout() calls it (5 mins from now). If your function needs arguments, you can reference the function in this way:
setTimeout(() => getTime(rightNow), FIVE_MINS)
In this case, I am passing in rightNow which is the datetime of when this file first started being parsed. So no matter how long setTimeout() waits, when it calls getTime() it will alert the user to the time it was when setTimeout started, pretty much.
Couple last things I'll mention is that this older syntax is equivalent to the example above:
setTimeout(function() {getTime(rightNow)}, FIVE_MINS)
You'd have to use the older syntax if you don't have a transpiler like Babel in your project to translate ES6 (new JS syntax).
And one last, last thing I'll mention, which is the most complex thing about callbacks, is that they can be called with arguments from within the function they have been handed over to, and you can intercept/add to/alter these arguments from the outer function before they get passed to the inner one if you need to. But I'd have to set up a much more complex example to show you that and I think your question only relates to the basic use of callbacks.

Passing an array of arguments

I'm trying to have a function that is passed a function and calls it with a set of args. For example:
function foo(func, args) {
return func(args);
}
However, I don't know how many elements might be in args, and func should be able to be an arbitrary function that takes any number of args. How do I deal with this?
Also say I wanted to store the functions passed to foo and only call them if they hadn't been called before.
Can I just do something like:
var calledFuncs = [];
function foo(func, args) {
if(calledFuncs.indexOf(func) === -1) {
calledFuncs.push(func);
return func(args);
}
}
Thanks, I'm a bit new to functional programming in JavaScript.
You're looking for func.apply:
the first argument is the context aka this value. You don't want to change this, so pass it through.
the second argument is an array containing the arguments, which can of course be of dynamic length.
So you could do this:
return func.apply(this, args);
You seem to have a fine approach to your second issue (with the func(args) replaced). You may want to store the return value of called functions, though, since foo now returns undefined for any calls except the first.
Use apply:
func.apply(this, args);
apply takes an array of arguments as the second argument.
The first argument to apply will be the value of this in the scope of the function that you are calling. So you can pass in the current this or anything else that you want.
As far as your second question goes, that will only work for named functions. You can use func.name to store the function name in your array:
var calledFuncs = [];
function foo(func, args) {
if(calledFuncs.indexOf(func.name) === -1) {
calledFuncs.push(func.name);
return func(args);
}
}
This won't work for anonymous functions and it doesn't work for named functions in IE or Opera. You're going to have to parse it out, perhaps like so:
var name = func.toString().replace(/\n/g, "").replace(/\s*\(.*$/, "").replace(/^\s*function\s+/, "");
As far as your second question goes, you can do what you're doing right now. But I don't think it would work for the following case:
foo(function() {
}, []);
foo(function() {
}, []);
It will call both of those functions.
You want Function.prototype.apply:
func.apply(this,args);
Set the context (the first argument) to whatever you want, including null to get the window global (as you would get with your current func(...) invocation.
Although not directly related to your question, see also the related Function.prototype.call method that allows you to similarly set the context, but pass explicit parameters.

Categories

Resources