I have a block of code like that
function doA (callback) {
//do something...
callback();
}
function doB (callback) {
//do something...
callback();
}
function doC () {
//do something...
}
i know it's a bad practice and have to avoid it, but i am trying to understand why when i called the functions as
doA(doB(doC)));
it throwed an error that "callback is not a function" ?
But when i tried:
doA(doC);
everything was ok.
edited: i have tried to use chrome dev tool to figure out how javascript callstack works here, and i found out that the call stack be like:
doC
doB
there isn't doA function, so why js avoids adding doA() to the callstack?
it throwed an error that "callback is not a function" ?
You call doA and pass the return value of doB(doC) as the first argument.
doB has no return statement, so it returns undefined.
doA tries to call the first argument (which has the value undefined) as a function, which it isn't, so it fails.
It isn't entirely clear what you are trying to achieve (since your example code has been abstracted so much), but you should probably investigate the Promises API which would let you do something like:
doA().then(doB).then(doC);
You are passing result of doB(doC) as the argument of doA but it is not a function:
doB(doC); // undefined
doA(undefined); // throws: callback is not a function
For callback you need to pass function and doB(doC) will execute it and will return undefined. And that undefined value will get passed to doA(undefined) and thats why you are getting -
callback is not a function
because doB it's not returning a function.
that expression means that the output of doB is passed as argument to doA, but since doB is not returning a function you get the error.
Related
I have a simple promise, but I wonder why in my catch method I only need to pass "console.log" and it will automatically log that error if it happens?
Does it have something to do with the fact that catch automatically gives us an error object or is it something else?
In your case, you would pass the function console.log to the catch method. console.log method just prints each parameter in the console. Inside the catch method, the passed function will be executed on a reject/error of the promise.
Here is an example of passing functions:
function a(method) {
method("Hello World");
}
a(console.log);
In this case, the console.log function is available as method in the a function as it has been passed as a parameter. This is the reason, the code prints Hello World in the console.
Hidden arguments to sum it up here's an example!
function test() {
if (arguments[0] == "works") {
console.log("COOL");
}
}
test("works");
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
downloadPhoto('http://coolcats.com/cat.gif', handlePhoto)
function handlePhoto (error, photo) {
if (error) console.error('Download error!', error)
else console.log('Download finished', photo)
}
console.log('Download started')
I know handlePhoto as a callback passed into downloadPhoto, but I'm confused with handlePhoto function itself, the first parameter is error, is that means js recognize it as an error? can I use "err" replace or other parameter name.And what is the second parameter in console.log means, I can't find exactly answer for it yet.
When downloadPhoto function invocation is completed, it calls handlePhoto with some parameters.
According to convention ( rule generally followed ), first parameter should always be error & rest can be result values.
function downloadPhoto(url , callback ){
if(gotImageFromRemoteSuccessfully){
callback(null,successResponse)
} else{
callback(whatWentWrong , null );
}
}
the first parameter is error, is that means js recognize it as an error?
No, it means that when that function is called, the first argument passed to it will be placed in a variable named error.
can I use "err" replace or other parameter name
Yes. You can call the arguments whatever you want according to the normal rules of argument naming.
And what is the second parameter in console.log
That is the second thing that you want to be logged. console.log takes any number of arguments and logs them all.
Thanks everyone, I think if I add callback && callback(error ,result) in side downloadPhoto function, it would be more clear for me. But there is a follow up question here I saw many functions which don't have callback && callback(), how could these function pass parameter to its callback function?
Here the function returns an anonymous function:
function respondWithResult(res, statusCode) {
statusCode = statusCode || 200;
return function(entity) {
if(entity) {
return res.status(statusCode).json(entity);
}
return null;
};
}
Why is it that we are returning an anonymous function here that returns a value? What advantages do we have and when to try something like this?
How and what is this anonymous function's argument entity is populated with? We passed res to respondWithResult and what exactly happens next that we got entity for an argument in the anonymous function? What value does this argument get populated with?
In case it gets populated with res only, what's wrong with directly doing this instead:
if(res){
//bla bla
}
return null
Edit: The function is called like this:
return Outlet.find().exec()
.then(respondWithResult(res))
.catch(handleError(res));
Now, res is what we pass to respondWithResult. Is that what the anonymous functions gets in the argument? If yes, what is the advantage?Why not use res directly?
Why is it that we are returning an anonymous function here that returns a value? What advantages do we have and when to try something like this?
This is generally done because it has access to the scope of the function it was declared within. i.e. the local variables statusCode and res. Without more context it isn't immediately obvious why that is useful here.
How and what is this anonymous function's argument entity is populated with?
It's a function argument. It gets populated with whatever gets passed to it when it is called (which isn't in the code you shared).
As you pointed out, the function is returned (not immediately executed). Some other code will call it later.
Now, res is what we pass to respondWithResult. Is that what the anonymous functions gets in the argument? If
No. The returned function is passed to then. When the promise resolves, the result is passed to it.
The documentation at https://github.com/pivotal/jasmine/wiki/Matchers includes the following:
expect(function(){fn();}).toThrow(e);
As discussed in this question, the following does not work, because we want to pass a function object to expect rather than the result of calling fn():
expect(fn()).toThrow(e);
Does the following work?
expect(fn).toThrow(e);
If I've defined an object thing with a method doIt, does the following work?
expect(thing.doIt).toThrow(e);
(If so, is there a way to pass arguments to the doIt method?)
Empirically the answer seems to be yes, but I don't trust my understanding of JavaScript scoping quite enough to be sure.
We can do away with the anonymous function wrapper by using Function.bind, which was introduced in ECMAScript 5. This works in the latest versions of browsers, and you can patch older browsers by defining the function yourself. An example definition is given at the Mozilla Developer Network.
Here's an example of how bind can be used with Jasmine.
describe('using bind with jasmine', function() {
var f = function(x) {
if(x === 2) {
throw new Error();
}
}
it('lets us avoid using an anonymous function', function() {
expect(f.bind(null, 2)).toThrow();
});
});
The first argument provided to bind is used as the this variable when f is called. Any additional arguments are passed to f when it is invoked. Here 2 is being passed as its first and only argument.
Let’s take a look at the Jasmine source code:
try {
this.actual();
} catch (e) {
exception = e;
}
if (exception) {
result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected));
}
This is the core part of the toThrow method. So all the method does is to execute the method you want to expect and check if a exception was thrown.
So in your examples, fn or thing.doIt will be called in the Jasmine will check if an error was thrown and if the type of this error is the one you passed into toThrow .
If it is used like this:
expect(myTestFunction(arg)).toThrowAnyError(); // incorrect
Then the function myTestFunction(arg) is executed before expect(...) and throw an exception before Jasmine has any chance of doing anything and it crashes the test resulting in an automatic failure.
If the function myTestFunction(arg) is not throwing anything (i.e. the code is not working as expected), then Jasmine would only get the result of the function and check that for errors - which is incorrect.
To alleviate this, the code that is expected to throw an error is supposed to be wrapped in a function. This is passed to Jasmine which will execute it and check for the expected exception.
expect(() => myTestFunction(arg)).toThrowAnyError(); // correct
Sadly, it seems that if I need to test a function that takes parameters, then I will need to wrap it with a function.
I would rather have preferred,
expect(myTestFunction, arg1, arg2).toThrow();
But I am ok with explicitly doing
expect(function(){myTestFunction(arg1, arg2);}).toThrow("some error");
FYI, note that we can also use a regex match on error:
expect(function (){myTestFunction(arg1, arg2);}).toThrowError(/err/);