Say I have the following code.
function myFunc(item) {
console.log(item);
}
function action() {
return myFunc("Hello World");
}
const myUncalledFunction = action;
myUncalledFunction(); // will console.log "Hello World"
Is there a way to make this code cleaner? I've looked into .apply() but it looks like that calls the function immediately as well.
The reason I'm wanting to do this is Chai's expect for throw requires you to pass in an uncalled function. But the function I'm wanting to test requires arguments to be passed in. So currently I just created a wrapper function that returns the function with the arguments passed in.
I feel like there has to be a better way to achieve this that is cleaner and simpler for someone looking at it.
Use .bind to bind arguments (and a calling context, if needed) to a function, which produces a new callable function in the process. Here, you can see this as basically allowing you to pass in arguments at one time, and then call the desired function with those arguments later:
function myFunc(item) {
console.log(item);
}
const myUncalledFunction = myFunc.bind(undefined, 'Hello World');
myUncalledFunction();
Or you might just use a concise arrow function
function myFunc(item) {
console.log(item);
}
const myUncalledFunction = () => myFunc('Hello World');
myUncalledFunction();
Related
I've seen a code sample like that in some tutorial:
getSomething(function onSomething(name) {
return renderSomething(name);
});
And that it is equal to the following:
getSomething(renderSomething);
I'm a newbie, and never seen anything like this before. I can't really understand how the above can be the same(without parameter on the renderSomething()).
I've tried to recreate something similar to the above code to better understand it and kind of failed. I'm showing you what I've tried and hopefully you can explain to me why the above is equivalent and the below two questions that came up.
Can you please explain to me:
Why parameter name is undefined?
How come the console.log('a') is printed before the console.log('c')?
var name = 'alex';
console.log('start');
function print(cb) {
console.log('a');
cb();
}
function renderName(name) {
console.log('b');
console.log(name);
}
print(function onPrint(name) {
console.log('name: ', name);
console.log('c');
return renderName(name);
});
To understand how those two statements are equivalent, you really need to look inside of the function and also to reflect on why we use callbacks in the first place. It's also important to understand the difference between function declarations (function getFullName), invocations (getFullName()) and references (getFullName).
We use callbacks to react to something that might happen later, usually the result of whatever has happened is of interest to us. So we create callbacks that receive that result.
Imagine you have a function getFullName, if it was synchronous you would just call it like this:
const fullName = getFullName('John', 'Doe');
And inside the function might simply be:
function getFullName(firstName, lastName) {
return firstName + ' ' + lastName;
}
But for some reason, this function is asynchronous, so you don't get the result directly, so we use a callback to get notified when the name is ready to use:
function getFullName(firstName, lastName, callback) {
// Do something that takes time, then with the result of that
callback(firstName + ' ' + lastName);
}
Now when we call this function, it looks like this:
getFullName('John', 'Doe', function (fullName) {
console.log(fullName);
});
So the getFullName function, will invoke our callback and pass it the value it created. Since your function takes 1 argument, the fullName will be in that argument, no matter what we name that argument. So we can replace our callback (that we've created as an anonymous function) with any other function that takes one argument, such as console.log, like this:
getFullName('John', 'Doe', console.log)
In both these cases, we're sending in a function reference to getFullName, in the first case it's a reference from a function we've just created, in the second it's a reference to a function in the global scope.
You could equally do this:
const myCallback = function(fullName) {
console.log(fullName);
}
getFullName('John', 'Doe', myCallback);
Note how this is different from doing something like this:
getFullName('John', 'Doe', myCallback()); <-- function invocation!
Here, because we are invoking/calling the function, it will execute, and the result of that will be passed as the third argument to getFullName, which isn't what we wanted.
Reading up on first class functions might give you more insight!
As others have mentioned, but for completion, in your example, you never pass some result to your callback, in this case you could pass the variable name or something else to simulate the result being created. And by flipping the order in which you call console.log and cb(name) your logs should make sense.
What you return from your callback function will never really be used, at least not in our example.
Pass the parameter to cb in print function
var name = 'alex';
console.log('start');
function print(cb) {
console.log('a');
return cb(name);
}
function renderName(name) {
console.log('b');
console.log(name);
}
print(function onPrint(name) {
console.log('name: ', name);
console.log('c');
return renderName(name);
});
As to your questions:
Why parameter name is undefined?
Because you didn't pass an argument when you called cb(). It would have worked if for instance you had done cb("John").
How come the console.log('a') is printed before the console.log('c')?
Because that is the very first thing that the print function does. It only calls your callback with cb() after having done that.
So basically why do I have to use this kind of method in these kind of situations in particular?
function Example(callback) {
// What's the purpose of both 'call()' and 'null'?
callback.call(null, "Hello")
}
Exemple(function(callback) {
alert();
})
I've figured it out this syntax in a open project code but I haven't found out why it works yet.
You don't need to use call() in this situation. call() is used when you need to set the value of this in a function. If you aren't doing that, just call the function like you would any other function. For example:
function Example(callback) {
// call the function passed in
callback("Hello")
// this also works, but there's no benefit here
// because the callback doesn't care about `this`
// callback.call(null, "Hello")
}
// pass a callback function to Example
Example(function(text) {
console.log(text);
})
You would use call in situations where the callback function needs a value for this. For example:
function Example(callback) {
let person = {name: "Mark"}
// set this in the callback to the person object
// and the execute it with the argument "Howdy"
callback.call(person, "Howdy")
}
Example(function(greeting) {
// the callback function depends on having this.name defined
console.log(greeting, this.name);
})
This looks pointless at the first glance, however, there might be valid reasons for this syntax, for example:
the callback relies explicitly on this being null and not some global default
the callback is not necessarily a function, it can be a "function-alike" object that provides a .call method
callback.call(... is used consistently through the application, no matter if this is needed or not
Are Javascript callbacks just anonymous functions sent as an argument in a function call?
For example,
mySandwich('ham', 'cheese', function() {
alert('Finished eating my sandwich.');
});
JavaScript callbacks are functions passed as values into an asynchronous function for the purpose of continuation.
Functions are values:
So in JavaScript, you can pass a functions around like values. You can reference a function in a number of ways:
Pass a literal anonymous function as the callback
doSomeWork(function (err, result) {
if (err) {
throw new Error(err);
} else {
console.log(result);
}
});
Pass a literal named function as the callback
doSomeWork(function magicalCallback(err, result) {
if (err) {
throw new Error(err);
} else {
console.log(result);
}
});
(Naming every function is a smart idea because you can see it in the stack trace)
Pass in the value of a variable which happens to be storing a function as the callback
var someFunction = function callItWhatYouWant(err, result) {
if (err) {
throw new Error(err);
} else {
console.log(result);
}
}
// reference the callback stored in the someFunction variable
doSomeWork(someFunction);
Pass in the function by referencing the function name as the callback
function callItWhatYouWant(err, result) {
if (err) {
throw new Error(err);
} else {
console.log(result);
}
}
// reference the callback function using the function name
doSomeWork(callItWhatYouWant);
Continuation?
Continuation is all about the next step. When you call a function which is asynchronous, it needs to notify you that it is done. The callback acts as the next step, i.e. the asynchronous function will call you back when it is done.
So a callback is just a function argument used for a particular purpose, that being, continuation.
Callback signature
There is no standard for which arguments a callback should take, but in the Node.js community we have adopted the general signature
function (err, result)
where err is an Error object if something bad happened, or null if things were successful. If things went bad result is generally undefined, otherwise it contains the result. So your callback is generally called by either
callback(new Error("oops"));
or
callback(null, result);
Also note that it's normal for the last parameter of an asynchronous function to be the callback parameter
function callLater(delay, args, callback) {
setTimeout(function () {
callback(null, args);
}, delay);
}
In your example: yes
But in m example: No
function myCallback()
{
alert('finished eating my sandwich');
}
mySandwich('ham','cheese', myCallback);
So, from you comment I think the real question is: What is an anonymous function? I did my best, but it is still early in the morning here, so don't shoot me.
Well, gooed question. Hard answer
when defining a function, it lives withing its scope. Scope? huh? Ok, let's start again.
when a webpage is loaded, the browser creates a window object. It then starts parsing everything you wrote in that document (let's assume HTML).
Every DOMElement it encounters, gets put into the window.document object. Every Element inside the widnow.document element is rendered/interpreted in your browser window.
Now, the browser encounters the following <script>:
var myVariable = 'So cool';
The browser sees var myVariable. This tells him to create a variable called myVariable in the current scope (again that word). the current scope is window (the window object the browser created). So it adds the variable to the window object:
window.myVariable === myVariable
The same goes for functions
function myAwesomeFunction()
{
alert('I am so cool');
}
Creates a function inside the current scope (window) with the name myAwesomeFunction. So again:
window.myAwesomeFunction === myAwesomeFunction
But what if I want to create a function I don't whant any other code to have access to? What if I want a function that should only exist when a certain specific button is clicked.
Well, enter anonymous functions. these are functions that are declared in the anonymous scope. they cannot be accessed using the window object:
myButton = document.getElementById('myButton'); // === window.document.getElementById('myButton');
myButton.addEventListener('click', function()
{
alert('I am so awesome');
});
Now, the function you passed into the eventlistener only lives inside the eventListener. It doesn't even have a name. So it's scope is 'the on click event of the button' but we can't access it from outside because it doesnt have a name. Thus, anonymous function.
A good read would be to google stuff like 'javascript scope', 'javascript window object', ... A lot of good articles give a better in depth explanation of all the words I threw at you.
Yes, except that they don't have to be anonymous functions, and there's a bit more to the definition of a callback. The other two answers have covered the details of what callbacks are for and anonymous functions well, but I have had similar confusion to yours when I was first learning so I wanted to add a missing piece. So, your example:
mySandwich('ham', 'cheese', function() {
alert('Finished eating my sandwich.');
});
I've noticed most callback explanations are like this- they don't include the declaration of the larger function, in this case mySandwich. But yes, the function this is being passed into does need to explicitly call the callback function for it to run. So your mySandwich function might look like this:
function mySandwich(meat, topping, callback) {
//some code here using meat and topping
//some code here about eating the sandwich
callback();
}
So your code passes those parameters, including the function, into the mySandwich function, which then invokes it. Because many times the top-level function we are using is a method from a library rather than part of our own code, we don't actually see the step where the callback is invoked, but it does still happen and doesn't just automatically execute itself.
So yes, a callback is really just a function being passed as a parameter to another function, and then it is invoked usually as the last thing to happen in that larger function. There's more than that to callbacks, but they operate by the same rules as other functions in that somewhere they have to be declared and somewhere they have to be called, otherwise they don't run.
I've been struggling for awhile with callbacks in general. I'm trying to go back to the basics to try to understand conceptually and this is what I understand so far (yeah, basic).
function RandHash (callback) {
ds_hash = Math.floor((Math.random()*10000)+1);
callback();
}
function CompiledHash (){
console.log(ds_hash);
}
var ds_hash;
RandHash(Compiled Hash);
Will yield the random number.
However, I'm lost as to how to get the "ds_hash" variable returned from the callback.
Seems this would work:
var ds_hash;
ds_hash = RandHash(Compiled Hash);
It doesn't. If I try to return the value something like:
function CompiledHash (){
return ds_hash;
}
It doesn't do anything.
Please help me with this. Seems I spend 90% of my time with node in callback debugging. I've built some decent applications but everything has been handled through the async library because of this mental block I have.
Thanks.
The first error you've made is that RandHash hasn't returned anything. There's no return statement present in the function, so var anything = RandHash(alsoAnything) will always result in anything being undefined.
Functions are first class variables
Functions can be used just as variables can, and passed round as arguments to functions. This is a powerful feature of javascript and something that you'll need to grok to use callbacks. Think of a function as a defined action, and you're just passing that action around.
Also, callbacks should be used to deal with the completion of a process. So the idea is, you know what is meant to happen after process A, and what it is meant to generate, so you can give process A a callback which will be called once A has terminated. Now the scenario here isn't appropriate for a callback situation. It would be much easier for you to do...
function RandHash () {
return Math.floor((Math.random()*10000)+1);
}
And then get your hash by just calling this function, like so...
var hash = RandHash();
You also want to be aware of javascripts variable scoping, as you're missing the var keyword when referencing ds_hash in the RandHash function which means the assignment defaults to global scope. This is probably what is responsible for confusing you, as your assignment of ds_hash in RandHash will be global and therefore available in CompiledHash, meaning some functions will still be able to access the ds_hash value despite this not being the correct, or proper way to do this.
Assuming you will eventually require asynchronous processing
function randHash (callback) {
var hash = /* do hash calculation */
callback(hash);
}
function compileHash (hash) {
/* do something using the hash variable passed to this function */
}
randHash(compileHash); // pass the compileHash function as the callback
You should pass your variables as arguments to the callback. That will deal with your scoping issues hopefully.
Also, small note, functions in javascript should typically be lowercase if they aren't going to be used with the new statement (ie, a javascript class).
To truly understand asynchronous behaviour, you should try it with something that is actually asynchronous, and passing values around, not just a global variable, as that will never work when you move on to asynchronous functions :
function RandHash (callback) {
setTimeout(function() {
var ds_hash = Math.floor((Math.random()*10000)+1);
callback(ds_hash); // execute callback when async operation is done
}, 300);
}
function CompiledHash(hash){ // pass hash
console.log(hash);
}
RandHash(function(returned_hash) { // this is the callback function
CompiledHash(returned_hash); // pass along the returned data
});
The callback function is executed once the asynchronous function, in this case setTimeout, has completed, and passes the data back as an argument.
The callback argument is just the function that is passes as an argument.
// regular function
function doStuff(argument) {
// where argument can be anything, also a function
}
// you can pass a string
doStuff('string');
// or a function
doStuff(function(argument_returned) {
});
// and passing a function you can execute that function with arguments in the original function
function doStuff(argument) {
var argument_returned = 'string';
argument(argument_returned); // executes the passsed function
});
Try this:
function RandHash (callback) {
ds_hash = Math.floor((Math.random()*10000)+1);
callback(ds_hash);
}
function CompiledHash(ds_hash){
console.log(ds_hash);
}
var ds_hash;
RandHash(CompiledHash);
For callbacks and functions, it's a bit difficult to understand variable scopes, and what you are doing is not really recommended. You can pass parameters into callbacks, and should get passed, but I would suggest building it like this:
function RandHash (callback) {
var ds_hash = Math.floor((Math.random()*10000)+1);
callback(ds_hash);
}
function CompiledHash(ds_hash){
console.log(ds_hash);
}
RandHash(CompiledHash);
A couple notes:
If you want to propagate the result of the callback then you need to return its output in RandHash
You don't need ds_hash to be a global variable if you're planning on returning it, anyway
The line RandHash(Compiled Hash); is a syntax error (notice the space in the variable name)
Try this:
function RandHash(callback) {
var ds_hash = Math.floor((Math.random() * 10000) + 1);
return callback(ds_hash);
}
function CompiledHash(ds_hash) {
console.log(ds_hash);
return ds_hash;
}
RandHash(CompiledHash);
Notice that there are no global variables. The callback returns a value and the function that executes the callback passes it along.
On a style note, you should only capitalize the names of functions that you intend to use as a constructor.
This will do
function RandHash (callback) {
var ds_hash = Math.floor((Math.random()*10000)+1);
callback(ds_hash);
}
var CompiledHash = function (ds_hash){
console.log(ds_hash);
}
RandHash(CompiledHash);
In javascript it's very popular for libraries/frameworks to let us define a callback function for post-processing of data.
eg.
load("5", function(element) {
alert(element.name);
});
I wonder how the load() function looks like to be able to let the user provide a callback?
Are there good tutorials for this?
Well, the load function could look like this:
function load(arg, callback) {
var element = { name: "foo " + arg }; // something to pass
if (typeof callback == 'function') {
callback(element);
}
}
With the typeof check we make sure that the callback argument is an object that we can invoke, a function.
Then your example:
load("5", function(element) {
alert(element.name); // Will show `"foo 5"`.
});
In JavasScript functions are first-class objects. That pretty much means they act like other built in types. You can assign them to variables, pass them into functions, etc.
This article is a helpful link explaining how functions as first-class objects work in JavaScript:
http://helephant.com/2008/08/functions-are-first-class-objects-in-javascript/
Joel Spolsky has a detailed and interesting explanation on some of the interesting things/ways you can use functions as first class objects in JavaScript: http://www.joelonsoftware.com/items/2006/08/01.html
Finally, since they're first class objects, functions can very easily accept other functions as parameters:
var load = function(callback) {
// load something here
callback();
}
function load(foo, callback) {
blah(foo);
callback();
}
function load( number, callback){
// do something
if (callback) {
callback();
}
}
Functions are just like normal types and can be passed as arguments:
function load(param1, callback) {
// call the callback parameter one second later
setTimeout(function() {
callback(param1);
}, 1000);
}
function(callback,argumentArray) {
var result = callback.apply(this,argumentArray);
}
Points to be noted:
this can be null. In that case if this is used in the callback implementation then it would point to the Global object.
argumentArray is actually a JavaScript Array of arguments required by the callback method.