Understanding control flow in Node.js applications - javascript

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.

Related

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

Node.js async functions

The following code should handle http request by produce a request message to some remote server (kafka broker) and wait for consuming a response for it. when a respond message arrive - it should be returned as an http respond (json or something).
router.get('/status', function(req, res, next) {
// init the producer
...
// 1st async function
producer.on('ready', function () {
// some code for generating payloads (data for a message)
...
// 2nd async function
producer.send(payloads, function (err, data) {
// some log of success sending message
...
// 3rd async function
consumer.on('message', function (message) {
// got some response message
res.send("message: " + message);
});
});
});
});
Can I make these sync together even tow it's not mine?
EDIT:
I'll try to be more clear. Consider the following code:
function boo() {
// part 1 - init some consumer
console.log("1. finish init");
// part 2 - This is async function. whenever messages will arrive - this function will be fetched.
consumer.on('message', function (message) {
console.log("2. message arrive!");
return message;
}
// part 3
console.log("3. end function");
return null;
}
Assume that part 2 happen after 1 second. The output will be:
1. finish init
3. end function
2. message arrive!
while my goal is to wait for the async message (part 2) and return it's value. How can I achieve that?
You can use async library.
async.series([
fn1,
fn2
], function (err, results) {
console.log(results);
});
Or you could use https://github.com/AndyShin/sequenty
var sequenty = require('sequenty');
function f1(cb) // cb: callback by sequenty
{
console.log("I'm f1");
cb(); // please call this after finshed
}
function f2(cb)
{
console.log("I'm f2");
cb();
}
sequenty.run([f1, f2]);
When asking this question, I was complete new to node.js. After watching Philip Roberts video I realized how JavaScript is actually work. Then, I've solved my issue with a global messageArray & a messageId counter. Each user request is saved in messageArray (with its relevant handlers objects for later response). Then, the message is sent via kafka into the internal system components. The user won't get respond until the message arrived back from the system). When a message arrived to kafka consumer (from the system components) - we extract the relevant id and respond back to the relevant user). Here is the code:
var messageId = 0;
var messageArray= [];
router.get('/status', function(req, res, next) {
var o = {id: messageId, req: req, res: res, next: next};
messageArray.push(o);
messageId++;
// send message with kafka producer into the system internal components - THE MESSAGE CONTAINS THE messageId!
});
consumer.on('message', function (message) {
// Extract the original messageId from the arrived message and look for it in the messageArray
var messageId = extractMessageId(message);
var data = dequeueMessageById(messageId);
// got some response message
data.res.send("message: " + message);
});
function dequeueMessageById(messageId) {
for (var i=0 ; i < messageArray.length ; i++) {
if (messageArray[i].id == messageId) {
var messageData = messageArray[i];
messageArray.splice(index, 1); // remove from array
return messageData;
}
} /* for */
return null;
}

Using Async waterfall in node.js

I have 2 functions that I'm running asynchronously. I'd like to write them using waterfall model. The thing is, I don't know how..
Here is my code :
var fs = require('fs');
function updateJson(ticker, value) {
//var stocksJson = JSON.parse(fs.readFileSync("stocktest.json"));
fs.readFile('stocktest.json', function(error, file) {
var stocksJson = JSON.parse(file);
if (stocksJson[ticker]!=null) {
console.log(ticker+" price : " + stocksJson[ticker].price);
console.log("changing the value...")
stocksJson[ticker].price = value;
console.log("Price after the change has been made -- " + stocksJson[ticker].price);
console.log("printing the the Json.stringify")
console.log(JSON.stringify(stocksJson, null, 4));
fs.writeFile('stocktest.json', JSON.stringify(stocksJson, null, 4), function(err) {
if(!err) {
console.log("File successfully written");
}
if (err) {
console.error(err);
}
}); //end of writeFile
} else {
console.log(ticker + " doesn't exist on the json");
}
});
} // end of updateJson
Any idea how can I write it using waterfall, so i'll be able to control this? Please write me some examples because I'm new to node.js
First identify the steps and write them as asynchronous functions (taking a callback argument)
read the file
function readFile(readFileCallback) {
fs.readFile('stocktest.json', function (error, file) {
if (error) {
readFileCallback(error);
} else {
readFileCallback(null, file);
}
});
}
process the file (I removed most of the console.log in the examples)
function processFile(file, processFileCallback) {
var stocksJson = JSON.parse(file);
if (stocksJson[ticker] != null) {
stocksJson[ticker].price = value;
fs.writeFile('stocktest.json', JSON.stringify(stocksJson, null, 4), function (error) {
if (err) {
processFileCallback(error);
} else {
console.log("File successfully written");
processFileCallback(null);
}
});
}
else {
console.log(ticker + " doesn't exist on the json");
processFileCallback(null); //callback should always be called once (and only one time)
}
}
Note that I did no specific error handling here, I'll take benefit of async.waterfall to centralize error handling at the same place.
Also be careful that if you have (if/else/switch/...) branches in an asynchronous function, it always call the callback one (and only one) time.
Plug everything with async.waterfall
async.waterfall([
readFile,
processFile
], function (error) {
if (error) {
//handle readFile error or processFile error here
}
});
Clean example
The previous code was excessively verbose to make the explanations clearer. Here is a full cleaned example:
async.waterfall([
function readFile(readFileCallback) {
fs.readFile('stocktest.json', readFileCallback);
},
function processFile(file, processFileCallback) {
var stocksJson = JSON.parse(file);
if (stocksJson[ticker] != null) {
stocksJson[ticker].price = value;
fs.writeFile('stocktest.json', JSON.stringify(stocksJson, null, 4), function (error) {
if (!err) {
console.log("File successfully written");
}
processFileCallback(err);
});
}
else {
console.log(ticker + " doesn't exist on the json");
processFileCallback(null);
}
}
], function (error) {
if (error) {
//handle readFile error or processFile error here
}
});
I left the function names because it helps readability and helps debugging with tools like chrome debugger.
If you use underscore (on npm), you can also replace the first function with _.partial(fs.readFile, 'stocktest.json')
First and foremost, make sure you read the documentation regarding async.waterfall.
Now, there are couple key parts about the waterfall control flow:
The control flow is specified by an array of functions for invocation as the first argument, and a "complete" callback when the flow is finished as the second argument.
The array of functions are invoked in series (as opposed to parallel).
If an error (usually named err) is encountered at any operation in the flow array, it will short-circuit and immediately invoke the "complete"/"finish"/"done" callback.
Arguments from the previously executed function are applied to the next function in the control flow, in order, and an "intermediate" callback is supplied as the last argument. Note: The first function only has this "intermediate" callback, and the "complete" callback will have the arguments of the last invoked function in the control flow (with consideration to any errors) but with an err argument prepended instead of an "intermediate" callback that is appended.
The callbacks for each individual operation (I call this cbAsync in my examples) should be invoked when you're ready to move on: The first parameter will be an error, if any, and the second (third, fourth... etc.) parameter will be any data you want to pass to the subsequent operation.
The first goal is to get your code working almost verbatim alongside the introduction of async.waterfall. I decided to remove all your console.log statements and simplified your error handling. Here is the first iteration (untested code):
var fs = require('fs'),
async = require('async');
function updateJson(ticker,value) {
async.waterfall([ // the series operation list of `async.waterfall`
// waterfall operation 1, invoke cbAsync when done
function getTicker(cbAsync) {
fs.readFile('stocktest.json',function(err,file) {
if ( err ) {
// if there was an error, let async know and bail
cbAsync(err);
return; // bail
}
var stocksJson = JSON.parse(file);
if ( stocksJson[ticker] === null ) {
// if we don't have the ticker, let "complete" know and bail
cbAsync(new Error('Missing ticker property in JSON.'));
return; // bail
}
stocksJson[ticker] = value;
// err = null (no error), jsonString = JSON.stringify(...)
cbAsync(null,JSON.stringify(stocksJson,null,4));
});
},
function writeTicker(jsonString,cbAsync) {
fs.writeFile('stocktest.json',jsonString,function(err) {
cbAsync(err); // err will be null if the operation was successful
});
}
],function asyncComplete(err) { // the "complete" callback of `async.waterfall`
if ( err ) { // there was an error with either `getTicker` or `writeTicker`
console.warn('Error updating stock ticker JSON.',err);
} else {
console.info('Successfully completed operation.');
}
});
}
The second iteration divides up the operation flow a bit more. It puts it into smaller single-operation oriented chunks of code. I'm not going to comment it, it speaks for itself (again, untested):
var fs = require('fs'),
async = require('async');
function updateJson(ticker,value,callback) { // introduced a main callback
var stockTestFile = 'stocktest.json';
async.waterfall([
function getTicker(cbAsync) {
fs.readFile(stockTestFile,function(err,file) {
cbAsync(err,file);
});
},
function parseAndPrepareStockTicker(file,cbAsync) {
var stocksJson = JSON.parse(file);
if ( stocksJson[ticker] === null ) {
cbAsync(new Error('Missing ticker property in JSON.'));
return;
}
stocksJson[ticker] = value;
cbAsync(null,JSON.stringify(stocksJson,null,4));
},
function writeTicker(jsonString,cbAsync) {
fs.writeFile('stocktest.json',jsonString,,function(err) {
cbAsync(err);
});
}
],function asyncComplete(err) {
if ( err ) {
console.warn('Error updating stock ticker JSON.',err);
}
callback(err);
});
}
The last iteration short-hands a lot of this with the use of some bind tricks to decrease the call stack and increase readability (IMO), also untested:
var fs = require('fs'),
async = require('async');
function updateJson(ticker,value,callback) {
var stockTestFile = 'stocktest.json';
async.waterfall([
fs.readFile.bind(fs,stockTestFile),
function parseStockTicker(file,cbAsync) {
var stocksJson = JSON.parse(file);
if ( stocksJson[ticker] === null ) {
cbAsync(new Error('Missing ticker property in JSON.'));
return;
}
cbAsync(null,stocksJson);
},
function prepareStockTicker(stocksJson,cbAsync) {
stocksJson[ticker] = value;
cbAsync(null,JSON.stringify(stocksJson,null,4));
},
fs.writeFile.bind(fs,stockTestFile)
],function asyncComplete(err) {
if ( err ) {
console.warn('Error updating stock ticker JSON.',err);
}
callback(err);
});
}
Basically nodejs (and more generally javascript) functions that require some time to execute (be it for I/O or cpu processing) are typically asynchronous, so the event loop (to make it simple is a loop that continuously checks for tasks to be executed) can invoke the function right below the first one, without getting blocked for a response. If you are familiar with other languages like C or Java, you can think an asynchronous function as a function that runs on another thread (it's not necessarily true in javascript, but the programmer shouldn't care about it) and when the execution terminates this thread notifies the main one (the event loop one) that the job is done and it has the results.
As said once the first function has ended its job it must be able to notify that its job is finished and it does so invoking the callback function you pass to it. to make an example:
var callback = function(data,err)
{
if(!err)
{
do something with the received data
}
else
something went wrong
}
asyncFunction1(someparams, callback);
asyncFunction2(someotherparams);
the execution flow would call: asyncFunction1, asyncFunction2 and every function below until asyncFunction1 ends, then the callback function which is passed as the last parameter to asyncFunction1 is called to do something with data if no errors occurred.
So, to make 2 or more asynchronous functions execute one after another only when they ended you have to call them inside their callback functions:
function asyncTask1(data, function(result1, err)
{
if(!err)
asyncTask2(data, function(result2, err2)
{
if(!err2)
//call maybe a third async function
else
console.log(err2);
});
else
console.log(err);
});
result1 is the return value from asyncTask1 and result2 is the return value for asyncTask2. You can this way nest how many asynchronous functions you want.
In your case if you want another function to be called after updateJson() you must call it after this line:
console.log("File successfully written");

Synchronously HTTPS GET with node.js

So I'm trying to preform a https GET with node.jsand I have the following code
function get(url) {
https.request(url, function(res) {
var data = "";
res.on('data', function (chunk) {
data += chunk;
})
.on('end', function(){
console.log(JSON.parse(data));
});
}).on('error', function(e) {
console.log(e.message);
}).end();
}
This code works fine and dandy except I need this function to return the data its logging
I know the recommended way to do this is to use callbacks, passing a callback function into get and then calling that function in the 'end' listener. But the problem is that this process needs to be synchronized and NOT pipelined because it causes data hazards and uses too much memory. On top of that, its is recursively called and is just one big headache to try and manage.
Basically, I'm trying to return JSON.parse(data) in the get function then the end listener is called, is that possible?
You can't synchronously return data using an asynchronous function to retrieve the data. Your get() function will return long before the https.request() has completed so you just can't do what you asked to do.
The usual design pattern for solving this involves passing in a callback function to your get() function that will be called when the data is available. This will involve restructing the caller of your function to handle an asynchronous response via a callback function.
There are some different choices in how you structure the callback, but here's the general idea:
function get(url, callback) {
https.request(url, function(res) {
var data = "";
res.on('data', function (chunk) {
data += chunk;
})
.on('end', function(){
callback("success", JSON.parse(data));
});
}).on('error', function(e) {
callback("error", e);
}).end();
}
Usage:
get("http://www.example.com/myurl", function(status, data) {
if (status === "success") {
console.log(data);
}
});
May I recommend Q. It is specifically designed to help you fight the famous pyramid of callbacks in JavaScript. I understand that callbacks can lead to less-readable code but you should not try to make synchronous get requests. It kind of defeats the advantages of node.js.
You can convert
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
});
to this -->
Q.fcall(promisedStep1)
.then(promisedStep2)
.then(promisedStep3)
.then(promisedStep4)
.then(function (value4) {
// Do something with value4
})
.catch(function (error) {
// Handle any error from all above steps
})
.done();

Unable to call functions with Require.js

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.

Categories

Resources