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); });
}
Related
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
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.
The function below first performs a synchronous comparison test == 0, and if it passes, returns some content, and if it doesn't pass, performs an asynchronous request. My intent is for the later to return some other content such as "something from post callback", but know I am doing things way wrong. Without changing the Ajax request to synchronous, is it possible to do this?
var value = function (test) {
if (test == 0) {
return 'value is zero ';
} else {
return $.post('/echo/html/', {
html: 'false ',
delay: .5
}, function (r1) {
console.log('r1', r1);
return 'something from post callback';
})
.done(function (r2) {
console.log('r2', r2);
return 'something from done callback';
});
}
}(1);
console.log(value);
https://jsfiddle.net/o5kq8he6/2/
Since you are already returning a promise from the ajax call, then from your synchronous comparison, just return a resolved promise. Then, both code paths return promises that are resolved with the terminal value and the caller can use the same code to process the result no matter which way it works internally. This is a common design pattern for code that is sometimes synchronous and sometimes asynchronous.
var myFunc = function (test) {
if (test == 0) {
return $.Deferred().resolve('value is zero ');
} else {
return $.post('/echo/html/', {
html: 'false ',
delay: .5
}).then(function (r2) {
console.log('r2', r2);
// this will be the return value of the promise
return 'something from ajax finished';
});
}
};
myFunc(1).then(function(value) {
// value is here either way
});
FYI, it does not make sense in your $.post() to use both a success handler function AND a .done() handler. If you're going to return a promise from the function (which is my recommendation), then you should use only promise handlers, not the success callback.
You may also need to understand that it does nothing useful to return a value from the success handler of an ajax call. That return value just goes back into the asynchronous bowels of the ajax infrastructure and is never used by anything.
Make it all async:
var value = function (test, callback) {
if (test == 0) {
callback('value is zero ');
} else {
return $.post('/echo/html/', {
html: 'false ',
delay: .5
}, function (r1) {
console.log('r1', r1);
callback('something from post callback');
})
.done(function (r2) {
console.log('r2', r2);
callback('something from done callback');
});
}
}(1, function(result) { console.log(result); } );
You can simulate an async call for the others and then use a callback for all:
var value = function (test, callback) {
if (test == 0) {
callback('value is zero ');
} else {
return $.post('/echo/html/', {
html: 'false ',
delay: .5
}, function (r1) {
console.log('r1', r1);
callback('something from post callback');
})
.done(function (r2) {
console.log('r2', r2);
callback('something from done callback');
});
}
}(1, myCallback);
function myCallback(result) {
// here will the result be
}
You can also use setTimeout to actually make the sync calls behave as async calls in case you should need that.
I want to be able to perform some logic within a callback function based on whether callback(true) or callback(false) was called in the preceeding function.
Example:
foo.doFunction = function (param, callback)
{
int a = 1;
int b = param;
if(a < param)
{
callback(false);
}
else
{
callback(true);
}
}
foo.doFunction(param, function()
{
if(true)
{
}
if(false)
{
}
});
Is what I am trying to achieve possible through the use of callbacks?
Thanks for your time.
Yes, though your callback function would need to read the argument by name or using the arguments array:
foo.doFunction(param, function(myParam)
{
if(myParam)
{
}
else
{
}
});
When I run the following codes, the alert popup displays undefined. I thought it would return either true or false instead. Please can someone explain how the checkLoginStatus() function excutes. Thanks.
function checkLoginStatus() {
$.get("func.php", {op:'login_status', r:Math.random()}, function(data) {
if (data == "Yes") {
showSalesView();
return true;
} else {
loginView();
return false;
}
});
}
alert(checkLoginStatus());
There's a couple things wrong.
One, you're performing an asynchronous call within the function, so by the time the call has come back, checkLoginStatus has already returned. It essentially looks like this:
function checkLoginStatus() {
$.get("func.php", {
op: 'login_status',
r: Math.random()
}, function(data) {
if (data == "Yes") {
showSalesView();
return true;
} else {
loginView();
return false;
}
});
// return undefined
}
Second, you're returning within the callback of another function, so that return affects the return value of the callback to $.get
You want to use callbacks. So,
function checkLoginStatus(callback) {
$.get("func.php", {
op: 'login_status',
r: Math.random()
}, function(data) {
if (data == "Yes") {
showSalesView();
callback(true);
} else {
loginView();
callback(false);
}
});
}
and then
checkLoginStatus(function(result) {
alert(result);
});
The AJAX call is asynchronous, so the function that you specify as callback will be executed when the response arrives. The code doesn't wait for the response to exit from the checkLoginStatus function.
You can use a callback method to display the result:
function checkLoginStatus(callback) {
$.get("func.php", {op:'login_status', r:Math.random()}, function(data) {
if (data == "Yes") {
showSalesView();
callback(true);
} else {
loginView();
callback(false);
}
});
}
checkLoginStatus(function(status){ alert(status); });
what you are seeing is the undefined (void) return from the .get() function,
notice carefully that .get function call contains another function as the third parameter ( first being the url, second being an anon object) that is the "callback" for the results of the .get function, this is called later when the results have been returned from the server.
The returned bool is being returned by the $.get functions callback, not checkLoginStatus.
function checkLoginStatus() {
$.get("func.php", {op:'login_status', r:Math.random()},
// Start Callback Function
function(data) {
if (data == "Yes") {
showSalesView();
return true;
} else {
loginView();
return false;
}
// End Callback Function
});
}