Sorry for the ambiguous title, I just didn't know what else to put. Im learning JS and in the video the person does this.
When he calls the addListAfterKeyPress method at the bottom, why does the function call work? I thought he would have to put in a argument for the addListAfterKeyPress to take in since it has an event parameter. But in the end the code works perfectly. And also, when can i call a function that takes a parameter without explicitly passing one like how he did it in the picture?
This is a so called callback.
addListAfterKeypress at line 30 is not a call to this function, it is just saying, whenever the event keypress is raised on this input element, call this function.
Who is calling the function is the DOM library, indeed. You just have to pass a pointer to the function to be called.
Even more, the addListAfterKeypress function expects an event parameter, which will be provided to the function by the DOM library.
I think it is a bit more understanding by looking at an annonymous function there.
input.addEventListener('keypress', event => {
// This will be run whenever the input raises an event of type 'keypress'
// Here, the DOM library provides me an event parameter
});
Edit on demand:
Be aware, I am using ES6 just for comfort.
Suppose having this function
const callback = () => {
console.log(`My callback function is being executed`);
};
I will be using setTimeout to mock the HTML events. setTimeout is a function that expects a callback to execute after n milliseconds.The first parameter is the callback itself, the second is the amount of milliseconds, n.
See https://www.w3schools.com/jsref/met_win_settimeout.asp
setTimeout(callback, 500); // After 500ms, function callback will be fired
Here, I am just passing a pointer to the setTimeout function. This pointer must point to a function that will be called by setTimeout.
Let's write our own function that takes a callback as a parameter.
const callback2 = (number) => {
console.log('I received this parameter', number);
};
const callbackWrap = (callback, n) => {
// Here, callback parameter is a function that I can invoke, actually
console.log('The wrapper will execute the function passed as parameter');
callback(n);
};
Now, we can call the wrapper pointing to the new callback (callback2).
callbackWrap(callback2, 3);
callbackWrap(callback2, 4);
Or, we can define our function directly on the parameter list.
That is, we are passing an annonymous function
// p is the parameter of the callback function
callbackWrap(p => {
// Since callbackWrap will call the function parameter
// by passing a single parameter,
// the function I am declaring, expects that parameter
console.log(`Received ${p} inside the annonymous function`);
}, 'Parameter');
So, to summerize a bit, just the same way you can declare a variable (let's say, of type number) and then pass it to a function as a parameter, you can declare a function to pass it the same way.
const normalParameter = 30;
const functionParameter = p => console.log('Another callback', p);
callbackWrap(functionParameter, normalParameter);
// Or maybe passing more complex parameters
const anotherParameter = [1, '2', {3: 4}];
callbackWrap(functionParameter, anotherParameter );
Hope it clarifies a bit.
Related
I'm working through a Javascript course and am working with the following code. I understand almost all of it but I'm struggling with following and understanding the logic flow through the code, particularly for the event objects and I want to make sure I'm super clear on this before continuing.
Almost the exact same question has been asked here by someone else with the same confusion but I can't make sense of any answers unfortunately.
Here's what I do understand so far:
A key gets pressed -> The debounce function returns (and runs) with parameters func and delay. The func parameter passed in is the onInput function in this case, (which as I understand it, gets an event object returned to it automatically (by Javascript) when the addEventListener fires).
However, onInput is run nested inside the debounce function with func.apply(null, args); so I'm confused as to how the event objects get created and passed through the flow of the code when keys are pressed?
My main question following from this, is how or where does return (...args) within the debounce get its spread parameters from?
Doesn't the debounce function get passed the event object in this case and not onInput? If so, how does onInput get access to the event object?
Here's the code:
const fetchData = async (searchTerm) => {
const response = await axios.get('http://www.omdbapi.com/', {
params: {
apikey: '6459bge8',
s: searchTerm
}
});
console.log(response.data);
};
const input = document.querySelector('input');
const debounce = (func, delay) => {
let timeoutId;
//Where is ...args getting it's values from?
return (...args) => {
console.log(args);
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(() => {
func.apply(null, args);
}, delay);
};
};
const onInput = (evt) => {
fetchData(evt.target.value);
};
input.addEventListener('input', debounce(onInput, 500));
Also I can't make sense of when I comment out the code within the returned function like this:
const debounce = (func, delay) => {
let timeoutId;
return (...args) => {
console.log(args);
// if (timeoutId) {
// clearTimeout(timeoutId);
// }
// timeoutId = setTimeout(() => {
// func.apply(null, args);
// }, delay);
};
};
The passed in func never runs but the console.log(args) still shows InputEvents in the console when a key is pressed suggesting the args are coming from elsewhere and not set by func.apply(null, args);?
The main thing to understand with your code is that the addEventListener() function isn't in charge of calling the debounce() function. The debounce() function is called when the addEventListener gets added to the input element, not when the input event occurs. This is because calling debounce() invokes the function, passing whatever it returns as the second argument to addEventListener(). With that in mind, you function can be re-written as this:
const inputHandler = debounce(onInput, 500); // debounce returns a function
input.addEventListener('input', inputHandler); // the returned function is used in the addEventListener function
So the function that is returned by debounce() is called when an input occurs (not the debounce function itself, as this is called when the addEventListener() method is called, which is immediately when the interpreter meets this line and not when an input occurs).
Doesn't the debounce function get passed the event object in this case
and not onInput? If so, how does onInput get access to the event
object?
With the above explanation in mind, the returned function from debounce() is what gets passed as the second argument to addEventListener(). As a result, the returned function acts as the callback and gets passed the event object, which it has accesses to through ...args. In the code-block above, that means inputHanlder gets passed the event object when it gets invoked by JS when an input event occurs. So debounce() never gets passed the event argument, it's the inner returned function that gets access to the event argument.
As the returned function (ie: the inner function in your code example), gets passed the event object, it can access it via args. The inner function then invokes/calls the onInput function with the event object using func.apply(null, args);.
As for your last example, the func function never runs as it is never called anywhere. It is passed into your function, but it never gets invoked/executed (unlike in the first example where it does get called using .apply()). The InputEvent still gets logged though, as the addEventListener() is what invokes the callback returned when the input occurs. As a result, the inner function still has access to the event object.
I am new to javascript, I have gone through tutorials about callbacks, but I don't see any that answers this, both method below offers the same results, the only difference I see is callback allows dynamically passing in a callback function.
Are there other advantages, I am missing?
Thank you.
Callback
function logOne(callback) {
setTimeout(() => {
console.log("one");
callback();
}, 1000);
}
function logTwo() {
console.log("two");
}
logOne(logTwo); // one, two
No Callback
function logOne() {
setTimeout(() => {
console.log("one");
logTwo();
}, 1000);
}
function logTwo() {
console.log("two");
}
logOne(); // one, two
Your first example is more flexible: You can use any callback, not just logTwo.
Both have their uses, it depends on whether you need the flexibility or whether logOne should be specifically tied to logTwo.
Callback of function: If you want to do some operation on some event
Like show time on click of a button. Then you override onclick
function for that button. So whenever (independent on time) that
button is clicked, that application internal framework will call
onclick event and your onclick function will be called.
Normal function : Every function is normal function
Calling a function is when you actually do the function.
Passing a function is when function A needs function B in order to
work. So when you call function A, you pass it function B as an
argument. In this case you are not calling function B, instead you are
providing it to function A so that function A can call it.
Your second example create a tight coupling between logOne and logTwo functions. This way you end up with a logOne function that can't be reused since it only works with one exact function.
By passing a callback, you make your function more flexible and generalized. Now it is able to work with a wide range of other functions as long as they have the same "shape" - same number of arguments in a same order.
Playing around with the .on('click', ) event and I get differing behaviour based on whether I supply an anonymous vs named function (the named function doesn't work). Is this a syntax error?
<div id="myID"> abc </div>
<script>
$("#myID").on('click',function(e){
console.log(e.type);
}); //works
function handle(e){
console.log(e.type);
}
$("#myID").on('click',handle(e)); //doesn't work
</script>
You need to replace
$("#myID").on('click',handle(e));
with
$("#myID").on('click',handle);
When you call a function, it is executed immediately. This happens when you do
$("#myID").on('click',handle(e));
You call the function, passing an event e which does not exist yet. What you want instead is giving jQuery a function that it should call when the user clicks on the element with the id myID.
This is possible in JavaScript because it has first-class functions. This means that if you create a function like this:
function handle(e){
console.log(e.type);
}
then you get a reference to the function that you just created. This reference is stored in a variable named handle. You could achieve the same if you do:
var handle = function (e) { // create a function and store a reference to it in a variable
console.log(e.type);
};
The function takes an argument e. This doesn't exist yet, it has to exist in the moment you call the function:
handle(e); // ReferenceError: e is not defined
You can pass the reference to that function to jQuery, which then calls your function when the user clicks the element. At that point, e still doesn't exist, because it will contain information about the event, which hasn't occured yet. It will look like this:
$("#myID").on('click', handle); // pass a reference to the handle function to jQuery
Now, handle doesn't get called, because you only pass a reference to the function. You could say that you pass the function as an argument to another (jQuery) function. This is called a callback function.
Edit
Note that all functions that were created above take e as their argument. The argument doesn't have to exist in the very moment you create the function. However, when you (or jQuery) call the function, you have to provide an argument so that the function can do its job.
It's the same with an unnamed function: you create the function, but the argument does not exist yet. When you (or jQuery) call the function, you have to provide an argument.
This means there is no essential difference. The only difference is that one function has a name, the other one doesn't. You could even do this:
$("#myID").on('click', function handle (e) { // pass a reference to the function, but do not call it
console.log(e.type);
});
... which has the same effect as:
$("#myID").on('click', function (e) { // pass a reference to the function, but do not call it
console.log(e.type);
});
... except that in the first example, you keep a reference to the function that you created in a variable called "handle". In the second example, you lose the reference to the function, and only jQuery will be able to use your function.
Edit end
Another example for that would be:
var testFunction = function (arg) {
console.log('My argument is:', arg);
};
var executeTwoTimes = function (callback) { // accept a callback function as the first argument
callback('foo'); // execute the callback function
callback('foo');
};
executeTwoTimes(testFunction); // pass a reference to testFunction
// or:
executeTwoTimes(function (a) { // pass a reference to an anonymous function
console.log(a + ' bar');
});
I hope I could make things clearer for you.
How does the callback in fs.readfile get called when using fs.readfile.bind(context,pathArgument) like so. //understandable because my task function knows the name of the callback parameter
async.series([function(callback){
//operation done callback()},...],finalCallback(err,result));
BUT
//not understandable
async.series([fs.someOperation.bind(null,firstArgument),...],finalCallback(err,esult))
I believe I understand partial application;however, it would look something like this. function(callback){ fs.someOperation(firstArgument, ????)}(asyncCallbackFunc) and then I have no idea how the second argument is called...
Thx, in advance for helping me clear this up.
All bind does is set the context of the callback. It is still a regular callback like any other. Except, it is explicitly told what this will be. Looks like in your case, it is set to null.
The bind function on function object allows you to set the context i.e the value of this inside the function body as well as allow you to create a partial function in case you pass some arguments while calling bind.
For example:
function add(a,b) {
console.log(this);
return a+b;
}
var newAdd = add.bind("hello world", 10);
The newAdd will be one argument function which gets added to 10 and the result is returned. Also when newAdd is called the "hello world" will be logged in console.
Now when your code says fs.readFile.bind(null, path) it means that the return function will be of one argument which is the callback for readfile i.e the return function will be of form function(callback) { ... } which is exactly what is required to be passed to async.series
The main idea in the code you posted is to create a partial function that accepts only callback so that it can be passed to async.series the null argument doesn't play any role but you need to pass a context argument to call bind function, hence a null is passed as context arg.
for (var key in obj[i]) {
dataDump[key] = textField.value;
var callback = function(zeKey){
return function(e){
dataDump[zeKey] = e.source.value;
};
}(key);
textField.addEventListener('change', callback);
}
When I load the window, this function gets called automatically, which I don't want and instead I want this to be called only when I do a change.
The main point is calling function(zeKey){...}(key). When you do so, key, which is a string is copied as a parameter (zeKey) to your anonymous function.
The following
var callback = function(zeKey){
return function(e){
dataDump[zeKey] = e.source.value;
};
}(key);
Calls the anonymous function with argument zeKey.
This anonymous function returns another function. This returned function is assigned to the callback.
If 1 what you mean by "the function is getting called" then this is expected behavior.
This entire code should be called only after DOM is ready. Place all these in a function and make sure the function is called only on window.onload or (jQuery's) .ready()
The function returned by the function will be called only during the callback.
Add these code once dom is created. If above code is inside a function, attach to window.load or write these code at the end of page.