I'm quite new to JS and I don't really understand callbacks and I have looked for a fix for this error but I can't seem to find it.
When I execute the code I get this error : TypeError: callback is not a function -> callback(false);
function doesLobbyExist(a, callback) {
lobbyExists(a, function(random_data) {
callback(random_data);
});
}
function lobbyExists(a, callback) {
if(lobbies.length > 0){
lobbies.forEach(function(l) {
if(l.lobbyName == a){
console.log(a+" exists!");
callback(true);
}
});
}else{
callback(false);
}
}
And I call it like this:
doesLobbyExist(a.ln, function(result) {
console.log(result);
});
P.S. the code goes through console.log(a+" exists!");
I'm unable to reproduce what you're seeing. Running your code under a couple of different conditions, I got the following results:
> var lobbies = [{ lobbyName: 'foo' }];
> doesLobbyExist('foo', console.log)
foo exists!
true
> doesLobbyExist('bar', console.log)
...
> var lobbies = [{ lobbyName: 'foo' }, { lobbyName: 'bar' }, { lobbyName: 'foo' }];
> doesLobbyExist('bar', console.log)
bar exists!
true
> doesLobbyExist('foo', console.log)
foo exists!
true
foo exists!
true
...
> var lobbies = [];
> doesLobbyExist('foo', console.log)
false
but there a couple of problems in your code:
lobbyExists only gives a false response if there are no lobbies to check, it's possible for it to call the callback multiple times if there are multiple lobbies with the same name, and it doesn't return anything if a lobby isn't found.
there's no easy way to break out of forEach. since you only want to call your callback function once, you'll want to either return or break once a lobby has been found. switching over to a for i loop allows us to do either one.
comparison using == is what's known as loose or abstract equality, which may cause errors down the road - you can read up on the difference between loose (==) and strict (===) equality here.
if the lobbyExists function is only iterating through an array, then there's no need to treat it as asynchronous and use a callback. synchronous callbacks are a thing, and are perfectly fine to use, but it's something to be aware of as you continue to develop.
with all that being said, these are the changes that I would suggest:
function doesLobbyExist(a, callback) {
lobbyExists(a, function(random_data) {
//possibly do other things to random_data...
callback(random_data);
});
}
function lobbyExists(a, callback) {
for(var i = 0; i < lobbies.length; i++) {
var l = lobbies[i];
if(l.lobbyName === a) {
console.log(a + " exists!");
callback(true);
return;
}
}
callback(false);
}
It seems to me you're overcomplicating things, callbacks are simple to understand when you get the basic concept. I'll just try to leave a little primer on callback functions here and perhaps you can figure out where you went wrong in your own implementation. Consider the following code:
function lobbyExists(a, callback) {
if (a === 1) {
callback(true);
} else {
callback(false);
}
}
lobbyExists(1, function(response) {
console.log(response);
});
lobbyExists(0, function(response) {
console.log(response);
});
In this case, you are passing the entire function(response) { console.log(response); } function as a reference in the variable callback in lobbyExists. This means when executing lobbyExists, the variable callback now refers to that function. When you say callback(true) you are therefore calling function(response) { console.log(response); } where response is true.
I executed the code snippets and it worked fine for me.
Run the code below.
var lobbies = [{lobbyName:"a"}]
function doesLobbyExist(a, callback) {
lobbyExists(a, function(random_data) {
callback(random_data);
});
}
function lobbyExists(a, callback) {
if(lobbies.length > 0){
var bool = false
lobbies.forEach(function(l) {
if(l.lobbyName == a){
bool = true;
}
});
callback(bool);
return false;
}
callback(false);
}
doesLobbyExist("a", function(result) {
console.log(result);
});
However I changed lobbyExists function a little since callback would not call if there was no match found.
If you couldn't make it work try changing the variable name of callbacks in each function
Ex : Instead of "callback" use "cb"
More about callbacks
Similar question on stack
Related
I need to check some conditions before calling functions and those functions are calling from different js files like index.js or app.js.
What will be the best way to check that conditions before calling others? Think about you are doing something like authentication, if user is authenticated then call the function. I need to check that condition for 30 functions.
Instead of making changes on the functions I need to write one function/attribute/whatever and that needs to effect others.
file1.js:
function1
function2
function3
...
function30
file2.js:
file1.function1();
file3.js:
file1.function15();
To give an example of my suggestion in the comments, this is a simple way to wrap each function with an auth check without adding too much clutter to your calls.
// Some mock up of you existing code
var authenticated = true;
function thing1() { alert(1); }
function thing2() { alert(2); }
function thing3() { alert(3); }
// New code
function checkAuthAndRun(func) {
if (authenticated) {
func();
} else {
console.log("User is not allowed to do this");
}
}
// Calling your functions
checkAuthAndRun(thing1);
checkAuthAndRun(thing2);
checkAuthAndRun(thing3);
Example with parameters
// Some mock up of you existing code
var authenticated = true;
function thing1() {alert(1); }
function thing2(a) { alert(a); }
function thing3(a, b) { alert(a + b); }
// New code
function checkAuthAndRun(func) {
if (authenticated) {
// This line will need to have as many arguments[x] parameters as the function
// with the most parameters that you will call through this method
func(arguments[1], arguments[2]);
} else {
console.log("User is not allowed to do this");
}
}
// Calling your functions
checkAuthAndRun(thing1);
checkAuthAndRun(thing2, "Parameter 1");
checkAuthAndRun(thing3, "Parameter 1", "Parameter 2");
If I've understood your problem then I guess you don't want if-else or switch to clutter your code and you want a cleaner way to do this. What I would've done is, made a configuration file and executed it in the following way:
import {function1, function2, function3, function4 ...} from './functions';
const FUNCTIONS_TO_RUN = {
auth: () => {
function1();
function2();
...
},
noAuth: () => {
function3();
function4();
...
},
....
};
while calling them, I would just do as :
FUNCTIONS_TO_RUN[conditionType]();
This is not the exact solution but an idea how you can implement it in a cleaner way.
conditionType can be auth or noAuth in this case.
You may use && to short-circuit the statement.
Like, condition && fun() , fun() will be called only when conditon is true.
This is how && operator works in JS. if the first expression is wrong then it won't execute the rest of them otherwise it returns the value returned by the following expression(s), of course after executing them.
function auth(condition) {
return condition;
}
function isOdd(n) {
console.log(n," is odd ",n %2 == 0);
}
auth(false) && isOdd(3); //won't call
auth(false) && isOdd(4); //won't call
auth(true) && isOdd(5);
auth(true) && isOdd(6);
I'm trying to create a bounce effect on an image after an synchronous AND an asynchronous call but can't figure out how to do it. The problem I'm having now is that I sometimes get both the bounce effect and afterwards isExecuted becomes true because the asynchronous event takes some time.
My code should work like this:
Iterate over each object in myEnum and execute the following
if myCondition1 is equal too myCondition2 set isExecuted = true
if above is not true, call an asynchronous method which evaluates some stuff, if it's true it will set isExecuted = true.
wait for all above is finished, then if isExecuted still is false, bounce the image.
Here's the code:
var isExecuted = false;
myEnum.each()
{
if (myCondition1 == myCondition2) { //Synchronous
isExecuted = true;
return false; //stop the iteration
}
else {
MyAsyncFunction(myCondition2, function(isTrue) { // Asynchronous
if (isTrue) {
isExecuted = true;
return false; //stop the iteration
}
});
}
});
// Execute this ONLY when above code is finished executing
if (isExecuted == false) {
BounceImage();
}
Please note that the async function is not always executed but the bounce check must always be executed if isExecuted is true.
This whole setup won't work as you want because you cannot stop the iteration from the asynchronous callback. Instead you have to process the array (or whatever myEnum is) asynchronously and do something afterwards. I highly recommend to learn about promises.
function process(array, cb) {
var i = 0;
function p(v) {
return new Promise((resolve, reject) => {
try {
// call the callback, passing in the current value and
// a callback to control execution
cb(v, function next(stop, result) {
if (stop) {
// if stop = true, abort the iteration and resolve the passed in value
resolve(result);
} else if (i < array.length) {
// else if there are more elements left, process them
resolve(p(array[i++]));
} else { // else resolve to the passed in value
resolve(result);
}
});
} catch(e) {
reject(e);
}
});
}
// start with the first element
return p(array[0]);
}
process([1,2,3], function(v, next) {
if (v == 2) {
return next(true, v);
}
next();
}).then(result => console.log(result));
Applied to your code it would look something like
process(myEnum, function(v, next) {
if (myCondition1 == myCondition2) {
return next(true, true);
} else {
MyAsyncFunction(myCondition2, function(isTrue) {
if (isTrue) {
return next(true, true);
}
next();
});
}
}).then(function(isExecuted) {
if (!isExecuted) {
BounceImage();
}
});
Of course you can also use an existing library that allows you to do this. There a many different (potentially more elegant) ways to achieve this.
Instead, use callbacks:
function asyncFunction (a, b, c, callback) {
...
callback();
}
asyncFunction(1, 2, 3, function () {
doSomethingOnceDone();
});
This is a very common practice. It's how most async Chrome APIS do it, just to name one thing.
I have this function:
waitForFreeAccnt.prototype.isMemberFree = function () {
var self = this;
self.api.getMemberInfo(function () {
var accType = self.api.connect.accountType;
console.log(accType);
if (accType === 'FREE') {
console.log('it is free');
return true;
} else {
console.log('it is not free');
return false;
}
});
};
I would like to wait till the account is free for up to 10 seconds with something like that:
var test = function () {
for (var start = 1; start < 10; start++) {
var result = self.isMemberFree();
console.log(result);
if (result) {
break;
} else {
self.api.pause(1000);
console.log('waiting');
}
}
};
But it doesn't work because self.api.getMemberInfo is asynch call. This is super frustrating with Javascript. Any other language it would be so simple to do. How do I force the for loop to wait for self.isMemberFree() to finish executing before proceeding with the loop?
Also to note, this is not in browser execution so I don't care about anything hanging.
When dealing with asynchronous code, you need to make use of callbacks. That is, if you want to do a() and b() in order but a() does something asynchronously, then you need to call b() from within a() once a() has a result. So not:
a(); // does something asynchronously
b(); // tries to use a()'s result but it isn't available yet
... but rather
a(b); // pass b to a() and a() will call it when ready
function a(callback) {
triggerAsyncFunction(function(result) {
if (result === something)
callback("a just finished");
});
}
Note that a() doesn't refer to b() by name, it just calls whatever function is passed in as an argument.
So applying that to your code, maybe something like this:
waitForFreeAccnt.prototype.isMemberFree = function (cbf) {
var self = this;
self.api.getMemberInfo(function () {
cbf(self.api.connect.accountType === 'FREE');
});
};
waitForFreeAccnt.prototype.testMemberXTimes = function(maxAttempts, callback) {
var attempts = 0;
var self = this;
(function attempt() {
self.isMemberFree(function(free) {
if (free)
callback(true);
else if (++attempts < maxAttempts)
setTimeout(attempt, 1000);
else
callback(false);
});
)();
};
this.testMemberXTimes(10, function(isFree) {
// the next part of your code here, or called from here
// because at this point we know we've tested up to
// ten times and isFree tells us the result
});
Note that the way I coded getMemberInfo() it is basically doing the same thing yours was, but instead of returning a boolean it is calling the callback function and passing the same boolean value that you were returning. (I've removed the console.log()s to make the code shorter.)
Note also that you could structure the above to use promises, but the end result will be the same.
You could return a Promise
waitForFreeAccnt.prototype.isMemberFree = function () {
return new Promise((reject, resolve)=>
// set a timeout if api call takes too long
var timeout = setTimeout(()=> reject(Error('API timeout')), 10000);
// make api call
this.api.getMemberInfo(()=> {
clearTimeout(timeout);
resolve(this.api.connect.accountType === 'FREE');
});
);
};
Then use it like this
whatever.isMemberFree().then(isFree=> {
if (isFree)
console.log('it is free');
else
console.log('it is not free');
})
// handle timeout or other errors
.catch(err=> {
console.log(err.message);
});
Building on naomik's answer, if you do it that way you can pretty easily use a for loop with it, using the (most likely) upcoming async/await feature - though it's not part of ES2015.
// Note "async" here! That will make "await" work. It makes the function
// return a promise, which you'll be able to either "await" or
// "test().then" later.
var test = async function () {
for (var start = 1; start < 10; start++) {
// Right here we're using "await" - it makes JavaScript *wait* for
// the promise that comes from self.isMemberFree() to be finished.
// It's really handy because you can use it in loops like "for" and
// "while" without changing the flow of your program!
var result = await self.isMemberFree();
console.log(result);
if (result) {
break;
} else {
self.api.pause(1000);
console.log('waiting');
}
}
};
For now you'll need to use a transpiler like Babel or Traceur before you can really use async/await, though. It's only supported in Microsoft Edge 14 right now.
And a big emphasis that what is returned from test() isn't whatever you directly return from inside it. If I do this:
var test = async function() { return 15; };
var result = test();
I'm not going to get 15 - I'll get a promise that will resolve as 15:
result.then(function(res) {
console.log(res); // 15
});
// or, use an async function again:
var main = async function() {
console.log(await res); // 15
};
main();
I don't have my work laptop today because it is Sunday, I'm coding this on sublime. Apologise if the syntax is a bit off.
To solve your problem I would recommend changing isMemberFree() to take in a callback function. This is because isMemberFree is async, and you will need a way to report the result after it has done the work.
Then change test function to use setTimeout API to wait a second.
Wrap the function call for isMemberFree() to be in a nested function and call it recursively, that way you'll have synchronize control over the async calls.
Look at the coding example:
waitForFreeAccnt.prototype.isMemberFree = function (done) {
var self = this;
self.api.getMemberInfo(function () {
var accType = self.api.connect.accountType;
console.log(accType);
if (accType === 'FREE') {
console.log('it is free');
return done(null, true);
} else {
console.log('it is not free');
return done(null, false);
}
});
};
var test = function () {
var testMembership = function(waitAttempt, isFree) {
if (isFree) {
return;
}
else if (waitAttempt > 10) {
// wait exceeded, do something.
return;
}
setTimeout(function() {
self.isMemberFree(function(err, isFree) {
testMembership(waitAttempt+=1, isFree);
});
}, /*total milliseconds in 1sec=*/1000);
}
testMembership(/*WaitAttempts=*/0, /*isFree=*/false);
};
What the above code does is that, presumably something has already been done to the member's account and now test function is called. So it waits for 1 second, then call isMemberFree function, this happens recursively until either isMemberFree() returns true OR the 10 seconds wait has been exceeded.
I'm trying to do several calls inside a loop to an asynchronous API until the value that I want (true in this case) is returned. The issue is that I don't want to keep looping after that value is found and I don't want to execute the lines after the loop without the value returned from it...but so far I can't get that working. I think I'm doing something wrong here, but "I can't get the whole picture".
function isSelected(element, callback) {
// This is a little bit confusing for me...can I just get the value from
// getDataAsync without using isSelected function?
Office.select(element).getDataAsync(function (asyncResult) {
var result = true;
// some logic here that may change 'result'
callback(result);
});
}
function delete(elements) {
var index = -1;
for (var i = 0, (i < elements.length) && (index < 0); i++) {
isSelected(elements[i], function(result) {
if (result) { index = i; }; // ...and I don't want to keep "looping"
});
}
// I want to execute these lines after the for-loop is done
// ...do something with the "correct" index value
}
Have you tried Kriskowal's Q? There's a nice function called Q#allSettled:
Q.allSettled(promises)
.then(function (results) {
results.forEach(function (result) {
if (result.state === "fulfilled") {
var value = result.value;
} else {
var reason = result.reason;
}
});
});
So basically this is how it would work in your case:
var promises = [];
for(/* conditions */) {
promises.push(/* async call which returns a promise */);
}
Q.allSettled(promises).then(function(results) {
results.forEach(function (result) {
var value;
if (result.state === "fulfilled") {
value = result.value;
// do something with "value"
}
});
});
allSettled just makes sure the then will be executed regardless of whether or not the promise was successful or not, and you can check the value of the object you retrieve from your async call.
I am recommending three ways to doing it.
Using just JavaScript.
Using Async library.
Using underscore library.
Here you can see the javascript implementation:
You can do something like that:
You need to track home many times, you call the function, and how many times the callback happened
function delete(elements) {
var index = -1;
var stack=0;
for (var i = 0, (i < elements.length) && (index < 0); i++) {
stack++ // Go up on each loop
isSelected(elements[i], function() {
stack--; //Go down each callback
index = i;
if(stack==0) afterAllFinish() //When it return to 0 mean all callback have finished
});
}
function afterAllFinish(){
// I want to execute these lines after the for-loop is done
// ...do something with the "correct" index value
}
}
Using other libraries:
Please take a look at http://underscorejs.org/#after for the underscore way to solve it.
Please take a look at https://github.com/caolan/async#parallel to see the async way to solve it.
I have an if statement in php:
if ( $isTrue && db_record_exists($id)) { ... }
else { ... };
The first condition is a true / false boolean check.
The second condition calls a function to see if a row exists in a database table and returns true or false.
I want to rewrite this conditional in Node JS so that it is non-blocking.
I have rewritten db_record_exists as follows...
function db_record_exists(id, callback) {
db.do( "SELECT 1", function(result) {
if (result) { callback(true); }
else { callback(false); }
);
}
...but I can't see how to then incorporate this into a larger if statement, with the boolean check. For example, the following statement doesn't make sense:
if (isTrue and db_record_exists(id, callback)) {
...
}
What is the "node" way to write this?
Any advice would be much appreciated.
Thanks (in advance) for your help.
Check the variable first, then check the result of the async call inside the callback.
if (isTrue) db_record_exists(id, function(r) {
if (r) {
// does exist
} else nope();
});
else nope();
function nope() {
// does not exist
}
You will need to use callbacks for the if and the else part. Then "nest" the and-conditions:
if ($isTrue) {
db_record_exists(id, function(result) {
if (result)
doesExist();
else
doesntExist();
});
else
doesntExist();
For convenience, you could wrap all that in a helper function (and if you need it multiple times, put in a library):
(function and(cond, async, suc, err) {
if (cond)
async(function(r) { (r ? suc : err)(); });
else
err();
})($isTrue, db_record_exists.bind(null, id), function() {
…
}, function() {
…
});
Maybe this way?
function db_record_exists(id, callback) {
db.do( "SELECT 1", function(result) { callback(result ? true : false); });
}