Callback not returning - javascript

I am for some reason seeing the last callback hit even though it does get inside that first if statement. Shouldn't it return in that first if when I do response(res)? Well it's not. For some reason it still continues on and hits the second response(res):
function find(response){
var res = {};
res.statusCode = 200;
var data = _gateway.find();
if(!data){
res.statusCode = 204;
res.body = null;
console.log("SETTING RESPONSE BODY TO NULL");
response(res);
}
console.log("SHHHHHHHHHHHHHOULD NOT GET HERE");
res.body = data;
response(res);
};

Calling the callback doesn't mean that the current function will cease execution at that point. It is the same as calling some other function. In your case, once the callback function returns a value, the control gets transferred to the find function only and it continues to execute the rest of the statements.
To fix this, you need to return from the find immediately after the callback returns, like this
...
if(!data){
res.statusCode = 204;
res.body = null;
console.log("SETTING RESPONSE BODY TO NULL");
response(res);
return; // return from the `find` function
}
...
You can even return the result of response, like this
return response(res);
so that the control will be transferred immediately from the find function.

After response(res) put return statement, otherwise it will try to execute remaining statements or put it in else part.

Related

jQuery AJAX get request doesn't work normally and the return value can't be displayed in the console

I am new to jQuery and I am trying to build something with AJAX. I tried to use the normal, basic XMLHttpRequest class and then $.ajax({...}) but none of them work.
function getNormal_XHR_Request(){
let request = new XMLHttpRequest();
// I also tried to make a variable here, assign it to the request.response and return it in the end
let returnValue = null;
request.addEventListener(
"load",
(event) => {
if(request.readyState === 4 && request.status === 200){
console.log(request); // this will work
console.log(request.response); // this will work too
// Assign the returnValue to the request.response
returnValue = JSON.parse(request.response); // JSON.parse(request.response) doesn't return undefined, it returns my object that I want to use, but even if I equal the returnValue to it, it will still return undefined
return JSON.parse(request.response);
}
},
false // dispatch event in the bubbling phase not in the capturing phase
)
request.open("GET", "scripts/users.json");
request.setRequestHeader("Accept", "text/json");
request.send();
return returnValue
}
This is the normal XMLHttpRequest() that I used. The problem is, that if I want to say for example
x = getNormal_XHR_Request()
so that I would have the JSON file in my x variable, it doesn't work and it returns undefined.
And the exact same things happens with $.ajax({...})
class XHR_REQUEST{
constructor(path){
this.path = path;
}
getRequest_XHR(requestHeaders){
this.requestHeaders = requestHeaders;
var self = this;
let returnValue = [];
$.ajax({
url : self.path,
dataType : "json",
headers : self.requestHeaders,
})
.done((data) => {
returnValue = data;
})
.fail((jqXHR, errorMessage, error) => {
console.log(jqXHR);
console.log(errorMessage);
console.log(error);
});
return returnValue;
}
};
It works the same as the normal function that I used above, everything that I do, returns undefined, even if request.response gives me the correct answer. If I try to console.log(request.response), I will get the object, if I try to return it and console.log() the result, it will give me back undefined.
Why ?
The result you are getting in both examples is completely normal. The reason you are getting an undefined value is because both requests are happening asynchronously. The correct place where to return something or take some action with the results you get are:
1) request.addEventListener('load', event => {} .... within the event here you can perform the action on a succesfull response.
2) with the $.ajax() call you do it within the done() promise handler.
Those are the right places to take proper action on the results. You have to modify your mindset to start using the responses from the asynchronous callbacks.
The problem there is that you are returning the result, even before the callback has some value filled in the variable.
There are new constructions this days to make asynchronous calls behave synchronously withe async-await constructions. But besides this I think you have to modify or adjust your code to react to the async results in a way it works.

How can I ensure an if loop waits for and receives a result before running the next if loop in sequence

I have a javascript function where if loops are not following each other sequentially. I need them to run one after the other. The second loop should not run until the first loop has finished, because it deals with the output of the first.
I use an if loop (loop 1) to call a function in a child iframe (this frame contains mapping elements, and I can't reasonably combine it with the parent frame). This part is working as intended.
The function in the iframe is triggered, and it makes a call to an external service, and awaits a response. When it receives a response it passes either "error" or "ok" back to the parent function by using the 'return' function. This part is working as intended.
The parent receives the response, sets a variable and then should continue on with the next if statement (loop 2) that does something else.
What actually happens is that loop 1 runs, then loop 2 also runs, and loop 2 returns results before loop 1 - which screws things up as loop 2 is meant to be dealing with thee results from loop 1.
jQuery(document).on('click', '.gv-button-delete', function (e) {
e.preventDefault(); //prevent submission
console.log('Intercepted delete button request');
var delLoc = "";
(function ($) {
var delLoc2 = $('.gv-button-delete').attr("href"); //capture default action, because we need the time-valid nonce
delLoc = delLoc2;
}(jQuery));
var objID = document.getElementById("input_4_40").value;
objID = parseInt(objID);
var iframe = document.getElementById("MapFrame1");
var result = "";
if (iframe) { //1st if loop that collects the answer
var iframeContent = (iframe.contentWindow || iframe.contentDocument);
var result = iframeContent.WriteLog(objID); //call the function from the iframe, and hopefully wait for a result.
console.log(result);
}
if (result == "error") { //the second if loop
console.log("Step 5")
console.log("There was an error with removing the feature");
} else if (result == "ok") {
console.log("Step 5")
console.log("The spatial delete completed correctly");
} else {
console.log("Step 5")
console.log("unexpected result of spatial delete")
}
});
the iframe code, becuase it's useful for context.
function WriteLog(objID){
var retireFeatureID = hotspots.getFeature(objID);
var successStatus = "";
retireFeatureID.feature.properties["SystemStatus"] = "Deleted";
hotspots.updateFeature(retireFeatureID.feature, function (err, response) { //This is a syncronous call to another service
if (err) {
console.log("Step 3")
console.log(err);
successStatus = "error";
console.log("successStatus is: " + successStatus);
} else {
console.log("Step 3")
console.log(response);
successStatus = "ok";
console.log("successStatus is: " + successStatus);
}
});
console.log("Step 4")
console.log("Updated the status of feature: " + objID);
console.log("child iframe has variable successStatus as: " + successStatus);
return successStatus;
}
What actually happens is that the console results look like:
Step 4
Step 5
Step 3
The second loop is returning before the first loop has finished and returned a result.
async-await might the answer to your problem.
Here is how it works.
You define a function that sends a response with some delay (maybe because of a network call or something).
async function f() {
// Make network call and return value
return value;
}
And you call this function with an await.
var valueRequired = await f();
if(valueRequired == something) {
doSomeWork();
}
I hope this was clear.
Reference: MDN
Do note that this is not compatible in older browsers, as this is a rather modern JS construct.
This might be due to how callbacks and the javascript event loop work in general, and steps 4 and 5 will be executed first before step 3.
The function response callback will be placed at the end of call stack, which causes the remaining code (step 4 onwards and second if loop) to be executed without waiting for the callback to complete despite the other service code being synchronous.
I would suggest you to either convert the service function to one with a direct return and if possible not using callbacks, or changing the WriteLog function to a callback function by adding a callback argument and invoking it once you get the response from the other service.
JavaScript Event Loop Explained
Why don't you add a flag. This flag can go right after console.log(result). The 2nd if block can be inside a while that does not allow for the code to proceed before this flag is true. This ensures that your 2nd if won't happen before the 1st.

define outer scope variable inside function

i am building validation for one of form's field serverside (expressjs) and doing following actions for that:
Read data from json file
Get property from it (Array)
Check if it contains every single element of user generated array and nothing more, for example:
[1,2,3,4,5]; (json array)
[1,2,3,4,5,6] (user generated array) //must return false
[1,2,3,4,5];
[1,3,4] //must return true;
[1,2,3,4,5];
[1,2,7] //must return false;
so i am using this code for that:
const contains = (arr1, arr2) => {
arr2.every(v => arr1.indexOf(v) !== -1)
}
var match;
fs.readFile('../tags.json', 'utf8', (err, data)=>{
var JsonData = JSON.parse(data);
var tagsArray = JsonData.tags;
console.log(tagsArray)
console.log(tags)
if(tagsArray instanceof Array){
console.log('tagsArray is array')
}
if(!contains(tagsArray, tags)){
match = false
}
else{
match = true
}
console.log(match + ' blah1')
});
console.log(match + ' blah2')
if(match == false){
return res.status(409).send({
message: 'Do not provide your own tags'
});
}
but it always returns false inside fs.readFile block because it returns undefined outside fs.readFile block, so this means that contains function return undefined (i tested it)
so what is the clue for this?
Thanks!
fs.readFile is asynchronous, so any code that depends on its result (the file being read) needs to go within your callback function. (The callback function is the (err, data) => { ... } part.)
Move the console.log(match + 'blah2') and if(match == false) { ... } parts inside of the callback (after the blah1 line).
You could also look into async or use fs.readFileSync which would allow you to avoid using callback functions.
Another side point, you will want to make sure you always reach a res.send() line, i.e. when match == true in your case. Otherwise your http request will not return when match is true.
Edit:
Here's a really basic structure for express, mostly pseudocode & comments, just to illustrate callbacks:
app.post('/tags', (req, res) => {
// your setup code here
fs.readFile('../tags.json', 'utf8', (err, data) => {
console.log('readFile has finished')
// now you have heard back from readFile
// check the err and send 500 if there was a problem
// otherwise work with the file in the var data
// any other db-related stuff also goes in here, which probably
// has its own callback you need to use
db.save(data, (err) => {
// db call is done, potentially with an error
// Here you can use `res` to send http response
})
// !! again here the db is still doing its work
})
// !! anything you add here will be executed before readFile is done
console.log('readFile is probably still at work')
})
I should also point out that you want contains to return the bool value, i.e. return arr2.every(...)
You can use async/await :
async function read(){
let data = await fs.readFile(<path>);
console.log(data); //You can use data everywhere within this scope
}

How to return promise from .then

I want to be able to chain promises in order to make code synchronous. My problem is that depending on result of first $http request I could either be wanting to send another or not.
In case if I choose not to send another $http request I don't need my second then() to do anything. But since my second then() doesn't know about all this and it's hanging there anyway so I figured I need to return from first then some fake dummy promise. But I would also like to recognize this case in second then() . I came up with returning $q.when('some value') from first case. Here is the code:
$http.get(url, params)
.then(function(res) {
res = res.data;
if (res.status == 'ok' && res.rows.length > 0) {
$scope.roomTypes = res.rows;
return $q.when({ isDummy: true }); //in this case I don't need to send another request
} else if (res.rows.length === 0 && $scope.request.roomType) {
return $http.get(url2, params2); //make second request and return then`able promise
}
}, function(res) {
throw 'Error';
})
.then(function(res) {
res = res.data;
if (res.status == 'ok') {
var roomType = {
room_type: res.roomType.id,
description: res.roomType.description
};
$scope.roomTypes.push(roomType);
} else if (res.isDummy) {
//ok that's our dummy promise
} else {
//format of response unexpected it means something went wrong
throw "error";
}
}, funcrtion(res) {
throw "some Error";
})
.catch(function(res) {...})
.finally(function() {...});
The thing is I want to see value with which promise was resolved ({isDummy: true}), but how do I do that? I get undefined in my res parameter.
res will be undefined here
.then(function(res) {
res = res.data;
...
because there's no data property on {isDummy: true} object.
I think the basic issue here is confusing promises with promise handlers. The success handlers should return a value, not another promise. When you are in a promise success handler you are 'wrapped' by the promise mechanism which takes you returned value and passes it on to the next handler. This means your second promise does not see the initial response but what the first promise returned.
The value is processed as it goes along unless you pass it as is.
For example
This all comes to that your line
return $q.when({isDummy: true});
Should be
return {isDummy: true};
The problem is the other case, where you want to continue to a next query. I would probably do one of the following:
1. Start in the first promise the handling (with the related logic from the first handler).
2. Pass on url2 and params - return({url: url2, params: params}) and handle them in the second promise.
Note the promise chanins can even break in the middle, if any of the handlers rejects, following success handlers will not be called, here is a simple example (make sure you open your devtools console to see the log).
Try to return any object except promise or deferred :) And its value will be passed to then. Like this:
return { isDummy: true };
Example code: https://jsfiddle.net/817pwvus/
From jQuery when documentation:
If a single argument is passed to jQuery.when() and it is not a Deferred or a Promise, it will be treated as a resolved Deferred and any doneCallbacks attached will be executed immediately. The doneCallbacks are passed the original argument.
If you want code sync, you can always write a long code block in the first then.
if you want to chain promise (Post:url1)->(Post:url2) and so on:
1.The return is useless.
2. Let's assume you have 2 $http promises you want to chain, both from a service called $users, for example, and they called GetUserAge and GetRelevantBars and the second query is based on the first one's results.
angular
.module("moduleA")
.controller("exmapleCtrl",exampleCtrl);
exampleCtrl.$injector=["$users"];
function exampleCtrl($users){
var vm=this;
vm.query = $users.GetUserAge().then( /*OnSuccess*/ GetUserAgeCB)
//Callback of the GetUserAgeCB;
function GetUserAgeCB(result){
$users.GetRelevantBars().then( /*OnSuccess*/ GetRelevantBarsCB);
/*continuation of CallBack code*/
}
//Callback of the GetRelevantBarsCB;
function GetRelevantBarsCB(result){
/*CallBack code*/
}
}
hope this is understandable..
Instead of returning a dummy value and ignoring that explictly, you rather should nest and attach the second handler only to the promise that needs it:
$http.get(url, params)
.then(function(res) {
var data = res.data;
if (data.status == 'ok' && data.rows.length > 0) {
$scope.roomTypes = data.rows;
return; // do nothing
} else if (res.rows.length === 0 && $scope.request.roomType) {
return $http.get(url2, params2)
.then(function(res) {
var data = res.data;
if (data.status == 'ok')
$scope.roomTypes.push({
room_type: data.roomType.id,
description: data.roomType.description
});
else
throw "error"; //format of response unexpected it means something went wrong
});
}
})
.catch(function(res) {…})
.finally(function() {…});

Returning a value from callback function in Node.js [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 5 years ago.
I am facing small trouble in returning a value from callback function in Node.js, I will try to explain my situation as easy as possible. Consider I have a snippet, which takes URL and hits that url and gives the output:
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
});
I tried to wrap it inside a function and return a value like this:
function doCall(urlToCall) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
return finalData;
});
}
Because in my Node.js code, I have a lot of if-else statement where value of urlToCall will be decided, like this:
if(//somecondition) {
urlToCall = //Url1;
} else if(//someother condition) {
urlToCall = //Url2;
} else {
urlToCall = //Url3;
}
The thing is all of the statements inside a urllib.request will remain same, except value of urlToCall. So definitely I need to put those common code inside a function. I tried the same but in doCall will always return me undefined. I tried like this:
response = doCall(urlToCall);
console.log(response) //Prints undefined
But if I print value inside doCall() it prints perfectly, but it will always return undefined. As per my research I came to know that we cannot return values from callback functions! (is it true)? If yes, can anyone advice me how to handle this situation, as I want to prevent duplicate code in every if-else blocks.
Its undefined because, console.log(response) runs before doCall(urlToCall); is finished. You have to pass in a callback function aswell, that runs when your request is done.
First, your function. Pass it a callback:
function doCall(urlToCall, callback) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
return callback(finalData);
});
}
Now:
var urlToCall = "http://myUrlToCall";
doCall(urlToCall, function(response){
// Here you have access to your variable
console.log(response);
})
#Rodrigo, posted a good resource in the comments. Read about callbacks in node and how they work. Remember, it is asynchronous code.
I am facing small trouble in returning a value from callback function in Node.js
This is not a "small trouble", it is actually impossible to "return" a value in the traditional sense from an asynchronous function.
Since you cannot "return the value" you must call the function that will need the value once you have it. #display_name already answered your question, but I just wanted to point out that the return in doCall is not returning the value in the traditional way. You could write doCall as follow:
function doCall(urlToCall, callback) {
urllib.request(urlToCall, { wd: 'nodejs' }, function (err, data, response) {
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
// call the function that needs the value
callback(finalData);
// we are done
return;
});
}
Line callback(finalData); is what calls the function that needs the value that you got from the async function. But be aware that the return statement is used to indicate that the function ends here, but it does not mean that the value is returned to the caller (the caller already moved on.)
Example code for node.js - async function to sync function:
var deasync = require('deasync');
function syncFunc()
{
var ret = null;
asyncFunc(function(err, result){
ret = {err : err, result : result}
});
while((ret == null))
{
deasync.runLoopOnce();
}
return (ret.err || ret.result);
}
If what you want is to get your code working without modifying too much. You can try this solution which gets rid of callbacks and keeps the same code workflow:
Given that you are using Node.js, you can use co and co-request to achieve the same goal without callback concerns.
Basically, you can do something like this:
function doCall(urlToCall) {
return co(function *(){
var response = yield urllib.request(urlToCall, { wd: 'nodejs' }); // This is co-request.
var statusCode = response.statusCode;
finalData = getResponseJson(statusCode, data.toString());
return finalData;
});
}
Then,
var response = yield doCall(urlToCall); // "yield" garuantees the callback finished.
console.log(response) // The response will not be undefined anymore.
By doing this, we wait until the callback function finishes, then get the value from it. Somehow, it solves your problem.

Categories

Resources