Unable to call functions with Require.js - javascript

i try to write an module for my node.js server with require.js that just returns the object i want to get from an url. But somehow i can't return the values i get with my method. The http.get... is performed after i return the value, so that i just get "undefined" but why?
Could you please help me? Sorry if that is an stupid questen but im really new to javascript node.js and require.js.
define(['http'], function(http){
console.log('Hi ich bin pullArchiveVolume');
var response = 1;
console.log('Log 1: ' + response);
http.get("http:...", function(res) {
var body = '';
res.on('data', function(chunk) {
body += chunk;
});
res.on('end', function() {
console.log("Log 2: " + response);
response = 2;
console.log("Log 3: " + response);
response = JSON.parse(body);
return response;
// console.log("Log 2 :", response);
// console.log("Got response: ", response);
});
}).on('error', function(e) {
console.log("Got error: " + e.message);
});
console.log("Log 4: " + response);
return response;
})
Console Output:
Hi ich bin pullArchiveVolume
Log 1: 1
log 4: 1
log 2: 1
log 3: 2
Thanks!

You can't have a function that makes an async call just return something (unless it's a promise).
You need to have your function take a callback parameter:
function foo(callback) {
doSomethingAsync(function(data) {
// fire callback, which is a function that takes an argument 'data'
callback(data)
});
}
Then you can use it like this:
foo(function(data) {
doStuffWith(data);
});

The reason why you get what you get is that http.get(...) only initiates a network operation. At some indefinite point in the future the callbacks you gave to it will be executed. However, http.get(...) returns right away, before its callbacks are run. (Hence, it is an asynchronous operation). So by the time you hit your return statement the network operation is not complete yet and you return the initial value you gave to your response variable. Eventually, the network operation completes, your success callback is called and then you update response to the value you really wanted.
As others have said, you need to use callbacks or promises. Or rethink your design.

Related

Javascript Async/Await - Cannot get it to work

My 1st post. I'm a Javascript 'hacker' (and feel much more comfortable with PHP, never gotten a hang of Javscript):
I've tried for hours trying callbacks, delays, now ASYNC/AWAIT:
Problem: AWAIT Code Not doing what I want.
I've referenced multiple examples and forums, but not turn to stack overflow in desperate frustration!
Note: Expanding on this existing tool: https://github.com/webismymind/editablegrid/tree/master/examples
(notice it says be sure to run AJAX in Synchronous)
DatabaseGrid.prototype.initializeGrid = function(grid) {
var self = this;
grid.setEnumProvider('id_Bolt', new EnumProvider({
// the function getOptionValuesForEdit is called each time the cell is edited
// here we do only client-side processing, but you could use Ajax here to talk with your server
// if you do, then don't forget to use Ajax in synchronous mode
getOptionValuesForEdit: **async** function (grid, column, rowIndex) {
var Thread_Code = grid.getValueAt(rowIndex, grid.getColumnIndex("Thread_Code"));
**let** result = **new** setBoltEnum(Thread_Code);
*console.log(Date.now() + " - 3 - " + **await** result.array);*
return **await** result.array;
*console.log(Date.now() + " - 4 - " + "ending function");*
}
}));
};
AJAX code it is calling:
setBoltEnum = function(Thread_Code){
*console.log(Date.now() + " - 1 - calling AJAX");*
result = $.ajax({
url: 'EnumHelper.php',
type: 'POST',
dataType: "html",
data: {
value: Thread_Code,
col_out: 'Descr',
col_search: 'Thread_Code',
tablename: 'Bolt_List'
},
success: function (response) {
result = JSON.parse(response);
*console.log(Date.now() + " - 2 - got AJAX response: " + result.resp);*
return result;
//_callback();
},
error: function(XMLHttpRequest, textStatus, exception) { alert("Ajax failure\n" + errortext); },
async: true
});
};
Output in Chrome:
1 - calling AJAX
3 - undefined
2 - Got AJAX response - ok
(4) is not outputted
How can I get code to run 1-2-3-4?
You can only usefully await a promise.
You call setBoltEnum with new so it is treated as a constructor function and returns an object that is an instance of setBoltEnum (this doesn't make any sense because that function doesn't do anything that would be useful to use a constructor function for). It certainly doesn't return a promise.
setBoltEnum then calls $.ajax (which runs asynchronously) and assigns the result to result (You don't seem to have declared result so it is a global… this is probably bad, especially when dealing with asynchronous code). It then does nothing with that variable. It has no return statement at all so even if you didn't call it with new it will wouldn't return a promise.
Then you return await result.array;. There's no point in awaiting as you return since you are going to be returning a promise anyway (because the function is async). Here, result is the return value of $.ajax() which won't have an array property so the value is undefined (not a promise).
The next line is console.log but you never reach it because you just returned.
Later the success function is called. This assigns a new value to result which might have an array property… but that also won't be a promise.
To fix this:
Don't use new when you aren't dealing with a constructor function
Don't use await when you aren't dealing with a promise or other thenable
Do return promises or other thenables from functions that work asynchronously
Don't mix callbacks (success / error) with promises (it just makes code hard to manage)
Do remember that $.ajax() returns a thenable (which you can await).
Below code ended up working and outputted 1-2-3. Note, I stumbled upon this (adding ASYNC/AWAIT in setBoltEnum).
setBoltEnum = async function(Thread_Code){
console.log(Date.now() + " - 1 - calling AJAX");
return await $.ajax({
url: 'EnumHelper.php',
type: 'POST',
dataType: "html",
data: {
value: Thread_Code,
col_out: 'Descr',
col_search: 'Thread_Code',
tablename: 'Bolt_List'
},
success: function (response) {
result = JSON.parse(response);
console.log(Date.now() + " - 2 - got AJAX response: " + result);
},
error: function(XMLHttpRequest, textStatus, exception) { alert("Ajax failure\n" + errortext); },
async: true
});
};
DatabaseGrid.prototype.initializeGrid = function(grid) {
var self = this;
grid.setEnumProvider('id_Bolt', new EnumProvider({
// the function getOptionValuesForEdit is called each time the cell is edited
// here we do only client-side processing, but you could use Ajax here to talk with your server
// if you do, then don't forget to use Ajax in synchronous mode
getOptionValuesForEdit: async function (grid, column, rowIndex) {
var Thread_Code = grid.getValueAt(rowIndex, grid.getColumnIndex("Thread_Code"));
result = await setBoltEnum(Thread_Code);
console.log(Date.now() + " - 3 - returning this: " + result);
return result;
//return expecting this { "br" : "Brazil", "ca": "Canada", "us" : "USA" }; //result;
}
}));

While looping a jquery ajax call

I want to control the number of ajax calls to a controller using a while loop.
var counter = 0;
$('#filter-form').submit(function (event) {
event.preventDefault();
alert("counter init = " + counter)
while (counter < 10) {
(function () {
$.ajax({
url: '/algorithm',
method: 'GET',
data: $('#filter-form').serialize() + "&counter=" + counter,
success: function (data) {
alert("The data is " + data);
setCounter(parseInt(data))
},
error: function (xhr, status, error) {
var err = eval("(" + xhr.responseText + ")");
alert(err.Message);
}
});
})();
}
alert("counter end = " + counter)
});
function setCounter(data) {
counter = data
}
Controller:
#RequestMapping(value = "/algorithm")
#ResponseBody
public String test(#RequestParam Map<String, String> allRequestParam) {
int counter = Integer.parseInt(allRequestParam.get("counter"));
counter++;
return Integer.toString(counter);
}
The controller basically just increments the counter and returns it and in the ajax success: it will set the global counter to that number.
When I do this, the page just freezes and I cannot click anything. I put the ajax call in a function for scoping but it still does not work. When I use a for loop, it seems the ajax does not invoke because I do not get any success or error alerts.
It doesn't work for a simple reason: the $.ajax call is asynchronous.
Take this example:
$(function() {
var t = 1;
console.log("Hey, the ajax will start! t's value: " + t);
$.ajax({
url: 'www.google.com.br',
method: 'GET',
success: function (data) {
t++;
console.log("We've received an answer! t's (incremented) value: " + t);
},
error: function (xhr, status, error) {
t++;
console.log("We've received an error! t's (incremented) value: " + t);
}
});
console.log("Hey, the ajax just ended.... Not really. t's value: " + t);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
The output is:
Hey, the ajax will start! t's value: 1
Hey, the ajax just ended.... Not really. t's value: 1
We've received an error! t's (incremented) value: 2
That's because the $.ajax call is nonblocking, thus is doesn't block the program until it is finished, allowing the program to keep on executing the next line of code and continue running the ajax task in the background.
It is a recurrent issue in SO, so instead of providing solutions again here I'll ask you to read more on the questions:
How do I return the response from an asynchronous call?
How can I get jQuery to perform a synchronous, rather than asynchronous, Ajax request?
What does Asynchronous means in Ajax?
while will block synchronously until its condition is reached. Even if responses come back, the response will be asynchronous; the current thread (the while loop) will keep blocking forever.
Don't block. I don't see any reason to use a loop in the first place - instead, simply test to see if the counter is greater than the allowed number, and if it is, return:
$('#filter-form').submit(function (event) {
event.preventDefault();
alert("counter init = " + counter)
if (counter >= 10) return;
If you wanted to make multiple requests in parallel on form submit, you could do that, but you would have to keep track of the counter client-side:
var counter = 0;
$('#filter-form').submit(function (event) {
event.preventDefault();
alert("counter init = " + counter)
while (counter < 10) {
counter++;
// ... make request
As others have said your problem is that the call is asynchronous. This simple example may give you some idea about how to control the flow. It should be simple enough to apply it to your case.
I am simulating what you need to make your code work. For the errors, I am passing back null but you should bubble up any errors that may occur and either halt execution or deal with them some other way.
var count = 0; // used to store your count
// This represents the function you are
// waiting on with your ajax calls
function waitOne(num, callback) {
setTimeout(() => {
callback(null, num);
}, 1000);
}
// This represents your ajax call
function callWaitOne(callback) {
waitOne(count, (err, num) => {
// Your result is here
console.log(num);
// Callback to let the control function
// know the ajax has returned
callback(null);
});
}
// This will control the calls
function printWaitOne() {
callWaitOne((err) => {
if (count < 10) {
count++;
// Only calls if its callback
// has been called.
printWaitOne();
}
});
}
printWaitOne();

Alexa node js (alexa-app-server) response.say not outputting speech

I've been having some trouble trying to output speech after getting data using request-reponse and processing it. For whatever reason in the callback function it wont output the text. The console.log(prompt) works yet it the response.say(prompt).shouldEndSession(false).send() doesn't. If you've any idea it'll be greatly appreciated.
test.getNumberStatus(number, function(err, message) {
console.log("In callback outside if statement");
if (err == null) {
console.log(message);
response.say(message).shouldEndSession(false).send();
} else {
console.log("Error");
rresponse.say(message).shouldEndSession(false).send();
}
});
stopInfo.prototype.getStopStatus = function(stopNumber, callback) {
var options = {
method: 'GET',
uri: ENDPOINT + '?stopid=' + stopNumber + '&maxresults=1&format=json',
json: true
};
requestPromise(options).then(function(stopStatusObject) {
if (true) { // check if error from dublin bus
console.log(stopStatusObject);
var template = _.template('busstopinfo <%= error %>');
var message = template({
'error': 'test'
});
callback(null, message);
}
}).catch(function(err) {
var message = "I didn\'t have data for stop number " + stopNumber;
callback(err, message);
});
};
I had a similar situation and found out resolve automatically sends your output which works for sync but not for async. Put add response.resolve in your troubled function to see if value is set to true before send(). If it is you already used the promise. I corrected mine by putting return in front of function name, following Asynchronous Handlers Example section from here

Promises in bluebird to control function order

I've been trying to understand promises for the server side for the last few days, but I've yet to find anything to work. Bluebird however seems to be a good alternative. Following the API I've tried to test it out but I get an error of: "Cannot call method 'then' of undefined". I looked for similar errors but I couldn't make mine work similarly.
This is my attempt with bluebird so far:
var Promise = require('bluebird')
var cheerio = require('cheerio');
var request = require('request');
// do I need this?
// var request = Promise.promisify(require('request'));
// Promise.promisifyAll(request);
function getA() {
$(path).each(function(i,e){
link = $(this).attr("href");
getB(...).then(function(urls) {
console.log('type of urls: ' + typeof(urls) + urls);
// getC();
}).then(function(urls) {
// getD();
});
});
}
function getB() {
return new Promise(function(fullfill, reject) {
request(selectedShow, function (err, resp, page) {
if (!err && resp.statusCode == 200) {
var $ = cheerio.load(page);
$(path).each(function(i,e){
stuff.push($(this).text());
}
}); // end each loop
} // end error and status check
fullfill(stuff);
}); // end request
}
function getC() {
// use 'stuff' array to make requests and fill another array
}
function getD() {
// use the arrays to build an object
}
First thing to note, you should return the promise in function getB.
Second, you're fulfilling the promise too early. Rather you should fulfill/reject after you get a response from your request:
function getB() {
return new Promise(function(fullfill, reject) {
request(selectedShow, function(err, resp, page) {
// at this point you have a response could be error/success
if (!err && resp.statusCode == 200) {
var $ = cheerio.load(page);
$(path).each(function(i, e) {
stuff.push($(this).text());
});
// fulfill here, since success
fullfill(stuff);
} else {
reject("error"); // usually reject on error
}
});
// TOO EARLY, COS YOU DON'T HAVE A RESPONSE YET!
// fullfill(stuff);
}); // end request
}
I don't expect this answer to absolutely resolve your issue, but it should help though. Feel free to ask for additional direction after applying the change above.
To answer OP's questions in the comments to this answer
You have to modify your implementation of getA to the following effect:
function getA() {
$(path).each(function(i,e){
link = $(this).attr("href");
getB(...).then(function(urls) {
// NOTE: the `urls` parameter will have same value as the
// `stuff` used in calling `fulfill(stuff)`
console.log('type of urls: ' + typeof(urls) + urls);
// getC();
// you may also call getD() here
}).then(function(urls) {
// this additional `.then()` chain is not necessary as you could
// have done what it is you wanted to do here inside the first `then(...)`
// getD();
});
});
}
Now about the order of execution; putting everything together - your original code snippet and applying changes I made to it in this answer...
When you call getA(), that will in turn call getB() and after getB() executes successfully then getC() and getD() will be executed respectively. In summary, the order is getA() --> getB() --if getB() executed with a successful outcome--> getC() --> getC().
Now let's define two other functions getWhatever and ohNo:
function getWhatever(){
console.log("whateeveeer!");
}
function ohNo(){
console.log("eeeeew!");
}
We will modify getA a bit:
function getA() {
$(path).each(function(i,e){
link = $(this).attr("href");
getB(...).then(function(urls) {
console.log('type of urls: ' + typeof(urls) + urls);
getC();
}).then(function(urls) {
getD();
}).catch(function(){
// you only reach this point if the outcome of getB was an error
ohNo();
});
getWhatever();
});
}
With this new change to getA here are the orders of execution for different outcomes:
when outcome of getB is success:
getA() --> getB() --> getWhatever() --> getC()--> getD()
when outcome of getB is success:
getA() --> getB() --> getWhatever() --> ohNo()
I hope all your concerns are now addressed fully.

Understanding control flow in Node.js applications

I am trying to understand control flow in Node.js applications. Specifically does control returns to the original function once callback method completes (like a callback stack in recursive calls). I wrote a simple program that make a GET call and return the data. Here is the program:
Code:
var async = require('async');
var http = require('http');
function getGoogleData(url, callback) {
http.get(url, function(response) {
if (response.statusCode == 200) {
var googleInfo = '';
response.on('data', function(chunk) {
console.log("receiving data... ");
googleInfo += chunk;
return;
});
response.on('end', function() {
console.log("End of data receive... ");
response.setEncoding('utf8');
return callback(null, googleInfo);
});
}
console.log("I am here but why!");
//callback(new Error("GET called failed status_code=" + response.statusCode));
});
console.log("Return from get google data");
}
async.waterfall([
function(callback) {
console.log("In func 1");
getGoogleData("http://www.google.com", callback);
},
function(data, callback) {
console.log("In func 2");
callback(data);
}],
function (err, res) {
console.log("In err fn");
});
Here is output of the program:
Output:
In func 1
Return from get google data
I am here but why!
receiving data...
receiving data...
End of data receive...
In func 2
In err fn
Can someone help me understand why 'I am here but why!' line gets printed as the second output line in console log even after returning from 'data' event emitter? What is the overall control flow here?
The reason you're seeing that message logged first is that all that the code inside the if block is doing is adding event handlers. Those events are emitted some time in the future, after your console.log has already executed.
It's a similar reason why "Return from get google data" gets printed before the request finishes, because the http request is asynchronous.

Categories

Resources