Are node.js variables none blocking - node.js

I have some node code and I'm trying to set this up but it seems like it's processing one item before another is complete and I'm wondering if I'm right and if so if there is some kind of work around
var input = function(text){
process.stdout.write(text);
process.stdin.setEncoding('utf-8')
process.stdin.once("data", function(input_data){
return input_data;
}).resume();
}
var test = function(var1, var2){
var var1 = (var1 == null) ? input("Var One: ") : var1;
var var2 = (var2 == null) ? input("Var Two: ").split(" ") : var1;
console.log(var1, var2);
}
Though, when I execute test(null,null) I expected it to ask me for Var one then define var1 as the data then after that I thought it would prompt me with Var Two and then split that by spaces into a list however that did not work and it just errored saying "Cannot call method of undefined "
I came from python where this was possible and it wouldn't do any other executions till the previously defined one was completed so I'm wondering if node has something similar to this and note I am not using a server or website for this I'm just testing code on my computer.

I'm fairly new to node myself, but here's my understanding of how it will execute:
test(null, null);
// enter test() function
// see that var1 == null, run input("Var One: ")
// write "Var One: " to stdout
// set stdin encoding to utf-8
// set an event listener for 'data' on stdin, and provide a callback for that event
... let's pause there for a moment, because this is the important bit.
When you set a listener and a callback, you've entered the land of asynchronicity. Your code will continue to march on without waiting and do the next things that you've told it to do. Which, in this case, is just sending back an undefined return from input(), and then moving on to handle var2 similarly, where you try to call undefined.split(), and the whole process pukes.
If, on the other hand, you just remove .split(), you'll probably get an output like this:
Var One: Var Two : [waiting for input]
At this point, when you enter the first value, it'll take it and return it from your callback to... nowhere... then it'll wait for your next value and do the same.
This is where you have to start to break your mind away form your python background and procedural, synchronous habits.
I'll risk beating a dead horse, and comment up your code directly:
var input = function(text){
process.stdout.write(text);
process.stdin.setEncoding('utf-8')
// .once() is an asynchronous call
process.stdin.once("data", function(input_data){
// the return value of this anonymous function isn't used anywhere
return input_data;
}).resume();
// there is no return value of the "input" function
}
Functionally, what you're looking for is something like the following (though this is ugly, something like async.waterfall makes this sort of structure much more palatable, and there may be even better ways to do it that I haven't learned yet):
function test(var1, var2) {
if (!var1 || !var2) {
process.stdin.resume();
process.stdin.setEncoding('utf-8');
if (!var1) {
process.stdout.write('Var One: ');
process.stdin.once('data', function(input_data) {
// var1 & var2 get pulled in from the parent context
var1 = input_data;
if (!var2) {
process.stdout.write('Var Two: ');
process.stdin.once('data', function(input_data) {
var2 = input_data;
console.log(var1, var2);
});
}
else {
console.log(var1, var2);
}
});
}
else if (!var2) {
process.stdout.write('Var Two: ');
process.stdin.once('data', function(input_data) {
var2 = input_data;
console.log(var1, var2);
});
}
else {
// there is no else, either var1 or var2 is missing
// per the first conditional
}
}
else {
console.log(var1, var2);
}
}

Here is how you can do it :
function input (text, val, cb) {
if (val) return cb(null, val)
process.stdout.write(text)
process.stdin.setEncoding('utf-8')
process.stdin.once('data', function(data){
process.stdin.pause()
cb(null, data)
}).resume()
}
function test (var1, var2) {
input('Var One: ', var1, function (err, var1) {
input('Var Two: ', var2, function (err, var2) {
console.log(var1)
console.log(var2)
})
})
}
test(null, null)
Basically, since stdin is async, so is input function. You need to use callback-based function style. This works, though you strongly recommend not using stdin this way. Try readline core module or some special userland modules from npm.
You can see that writing callback based code can be a little messy (aka callback hell). Here is a fancy way to address this issue using co module (you need node 0.11.x for this and use --harmony-generators flag):
var co = require('co')
function input (text) {
return function (cb) {
process.stdout.write(text)
process.stdin.setEncoding('utf-8')
process.stdin.once('data', function(data){
process.stdin.pause()
cb(null, data)
}).resume()
}
}
function * test (var1, var2) {
var1 = var1 || (yield input('Var One: '))
var2 = var2 || (yield input('Var Two: '))
console.log(var1)
console.log(var2)
}
co(test)(null, null)

I would use this built-in Node.js module: http://nodejs.org/api/readline.html
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question("What do you think of node.js? ", function(answer) {
// TODO: Log the answer in a database
console.log("Thank you for your valuable feedback:", answer);
rl.question("What do you think of JavaScript?", function(answer2) {
console.log("Answer2 is ", answer2);
});
rl.close();
});
There is a relatively new language derived from CoffeeScript that makes async code more readable. It basically looks like sync code, and you don't have to nest indents for callbacks all the time. Its called ToffeeScript https://github.com/jiangmiao/toffee-script Coming from Python you may appreciate it.
readline = require 'readline'
rl = readline.createInterface { input: process.stdin, output: process.stdout }
answer = rl.question! "What do you think of node.js?"
console.log "Thank you for your valuable feedback:", answer
answer2 = rl.question! "What do you think of ToffeeScript?"
console.log "Thanks again."
rl.close()

Related

Q promise - how to use it?

I am new to Node and want to call a function and determine whether or not to call the next function based on the results of the first function. The functions are asynchronous so the results are not immediately known. The following code works fine but now I want to add more asynchronous functions and chain them all together.
var doSomething = require('./custom-module1.js'); //returns a boolean
var doAnotherThing = require('./custom-module2.js'); //returns a double
var var1 = process.argv[2];
var var2 = process.argv[3];
var var3 = process.argv[4];
doSomething(var1, var3, function(data) {
if (data) {
doAnotherThing(var1,var2, function(data){
console.log(var1 + ' is value: ' + data);
});
}
});
I want to make the code something like this using promises.
getListOfStuff()
.then(
for(i=0;i<data.length;i++) {
doSomething(data[i], var3)
.then(
if (doSomething returns true) {
doAnotherThing(data[i], var2)
.then(doYetAnotherThing(data[i]))
.then(andEvenMoreThingsToBeDone(data[i]))
}
);
}
);
I read about q and using denodefiy but I can't understand the syntax to make it work. Would it be best to modify my custom modules to return promises so they would be inherently thenable? I get the concept but not the syntax, how do I actually do it?
I tried to denodeify using the following 2 different notations.
var doSomethingDN = Q.denodeify(doSomething);
var doAnotherThingDN= Q.denodeify(doAnotherThing);
doSomethingDN(var1, var3)
.then(
if(!data) {
doAnotherThingDN(var1,var2)
}
);
And this one
var doSomethingDN = Q.nfbind(doSomething);
var doAnotherThingDN= Q.nfbind(doAnotherThing);
doSomethingDN (var1, var3)
.then(
if(!data) {
doAnotherThingDN(var1,var2)
}
);
The if get's an unexpected token error. If I remove then both run but the boolean result from doSomething isn't able to control the program flow.
You still need to use a callback function with promises:
doSomethingDN (var1, var3)
.then(function(data) {
// ^^^^^^^^^^^^^^^^
if (!data) {
return doAnotherThingDN(var1,var2)
}
}); /*
^
You were getting a syntax error because you put the function body right as the argument to then.

how to work with "process.stdin.on"?

I'm trying to understand process.stdin.
For example - I need to show array elements in console. And i should allow user choose which element will be shown.
I have code:
var arr = ['elem1','elem2','elem3','elem4','elem5'],
lastIndx = arr.length-1;
showArrElem();
function showArrElem () {
console.log('press number from 0 to ' + lastIndx +', or "q" to quit');
process.stdin.on('readable', function (key) {
var key = process.stdin.read();
if (!process.stdin.isRaw) {
process.stdin.setRawMode( true );
} else {
var i = String(key);
if (i == 'q') {
process.exit(0);
} else {
console.log('you press ' +i); // null
console.log('e: ' +arr[i]);
showArrElem();
};
};
});
};
Why the "i" is null when i type number a second time? How to use "process.stdin.on" correctly?
You're attaching a readable listener on process.stdin after every input character, which is causing process.stdin.read() to be invoked more than one time for each character. stream.Readable.read(), which process.stdin is an instance of, returns null if there's no data in the input buffer. To work around this, attach the listener once.
process.stdin.setRawMode(true);
process.stdin.on('readable', function () {
var key = String(process.stdin.read());
showArrEl(key);
});
function showArrEl (key) {
console.log(arr[key]);
}
Alternatively, you can attach a one-time listener using process.stdin.once('readable', ...).
This is typically how I get input when using stdin (node.js) This is the ES5 version, I don't use ES6 yet.
function processThis(input) {
console.log(input); //your code goes here
}
process.stdin.resume();
process.stdin.setEncoding("ascii");
_input = "";
process.stdin.on("data", function (input) {
_input += input;
});
process.stdin.on("end", function () {
processThis(_input);
});
hope this helps.

Block for stdin in Node.js

Short explanation:
I'm attempting to write a simple game in Node.js that needs to wait for user input every turn. How do I avoid callback hell (e.g. messy code) internal to a turn loop where each turn loop iteration needs to block and wait for input from stdin?
Long explanation:
All the explanations I have read on StackOverflow when someone asks about blocking for stdin input seem to be "that's not what Node.js is about!"
I understand that Node.js is designed to be non-blocking and I also understand why. However I feel that it has me stuck between a rock and a hard place on how to solve this. I feel like I have three options:
Find a way to block for stdin and retain my while loop
Ditch the while loop and instead recursively call a method (like nextTurn) whenever the previous turn ends.
Ditch the while loop and instead use setTimeout(0, ...) or something similar to call a method (like nextTurn) whenever a turn ends.
With option (1) I am going against Node.js principles of non-blocking IO.
With option (2) I will eventually reach a stack overflow as each call adds another turn to the call stack.
With option (3) my code ends up being a mess to follow.
Internal to Node.js there are default functions that are marked **Sync (e.g. see the fs library or the sleep function) and I'm wondering why there is no Sync method for getting user input? And if I were to write something similar to fs.readSync how would I go about doing it and still follow best practices?
Just found this:
https://www.npmjs.com/package/readline-sync
Example code (after doing an npm install readline-sync)
var readlineSync = require('readline-sync');
while(true) {
var yn = readlineSync.question("Do you like having tools that let you code how you want, rather than how their authors wanted?");
if(yn === 'y') {
console.log("Hooray!");
} else {
console.log("Back to callback world, I guess...");
process.exit();
}
}
Only problem so far is the wailing of the "That's not how node is meant to be used!" chorus, but I have earplugs :)
I agree with the comment about moving towards an event based system and would ditch the loops. I've thrown together a quick example of text based processing which can be used for simple text games.
var fs = require('fs'),
es = require('event-stream');
process.stdin
.pipe(es.split())
.on('data', parseCommand);
var actionHandlers = {};
function parseCommand(command) {
var words = command.split(' '),
action = '';
if(words.length > 1) {
action = words.shift();
}
if(actionHandlers[action]) {
actionHandlers[action](words);
} else {
invalidAction(action);
}
}
function invalidAction(action) {
console.log('Unknown Action:', action);
}
actionHandlers['move'] = function(words) {
console.log('You move', words);
}
actionHandlers['attack'] = function(words) {
console.log('You attack', words);
}
You can now break up your actions into discrete functions which you can register with a central actionHandlers variable. This makes adding new commands almost trivial. If you can add some details on why the above approach wouldn't work well for you, let me know and I'll revise the answer.
ArtHare's solution, at least for my use case, blocks background execution, including those started by a promise. While this code isn't elegant, it did block execution of the current function, until the read from stdin completed.
While this code must run from inside an async function, keep in mind that running an async function from a top-level context (directly from a script, not contained within any other function) will block that function until it completes.
Below is a full .js script demonstrating usage, tested with node v8.12.0:
const readline = require('readline');
const sleep = (waitTimeInMs) => new Promise(resolve => setTimeout(resolve, waitTimeInMs));
async function blockReadLine() {
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
let result = undefined;
rl.on('line', function(line){
result = line;
})
while(!result) await sleep(100);
return result;
}
async function run() {
new Promise(async () => {
while(true) {
console.log("Won't be silenced! Won't be censored!");
await sleep(1000);
}
});
let result = await blockReadLine();
console.log("The result was:" + result);
process.exit(0);
}
run();

Using Q to create an async user input sequence

I am toying with Q and promptly and I am trying to ask, in a sequence, the user to input some stuff. For example :
What is your name? Bob
What is your age? 40
Hello Bob (40)!
(yes! it's a simple "Hello world!" program.)
And here is the code I am trying, directly from Q's github project page :
Q.fcall(promptly.prompt, "What is your name? ")
.then(promptly.prompt, "What is your age? ")
.done(function(name, age) {
console.log("Hello " + name + " (" + age + ")");
});
});
But it is not working as expected (maybe I'm reading wrong?). Whatever I try, it seems that promptly.prompt is listening to keystroke in parallel, and the .done function is called right away, resulting into a
/path/to/node_modules/promptly/index.js:80
fn(null, data);
^
TypeError: undefined is not a function
at /path/to/node_modules/promptly/index.js:80:9
...
once I hit Enter. Any idea why this is doing so and how I can accomplish what I'm trying to do?
** Edit **
Basically, what my end goal would be to create a reusable function invoked like so :
promptAll({
'name': "What is your name? ",
'age': "What is your age? "
}).done(function(input) {
console.log(input); // ex: { name: "Bob", age: 40 }
});
** Update **
Here's my working solution, I had to use nfcall as suggested by WiredPraine :
function multiPrompt(args) {
function _next() {
if (keys.length) {
var key = keys.pop();
Q.nfcall(promptly.prompt, args[key]).done(function(value) {
result[key] = value;
_next();
});
} else {
def.resolve(result);
}
};
var def = Q.defer();
var keys = _.keys(args).reverse();
var result = {};
_next();
return def.promise;
};
(Note : I am using Underscore, but the same can be achieved with a standard object iterator.)
Below are two approaches.
First, you'd need to use nfcall so that Q uses the NodeJS conventions for callbacks.
But, as the functions aren't promises, you'll need to handle the chaining and synchronous behavior slightly differently.
In the first example, start1, the code creates an instance of defer and returns it as the promise. When the prompt function returns, it resolves the deferred object instance and passes the value of the function (ideally the prompt). It should also handle errors, etc. in "real" code.
In both examples, I've added a function to grab the result of the the promise resolving. It's not passed as parameters to the last done instance. The function passed to done will execute as soon as the first promise is resolved (after the prompt has returned in this case).
var promptly = require('promptly');
var Q = require('q');
// make a simple deferred/promise out of the prompt function
var prompter = function(text) {
var deferred = Q.defer();
promptly.prompt(text, function(err, value) {
deferred.resolve(value);
});
return deferred.promise;
};
// this option just uses the promise option to prompt for name.
function start1() {
prompter("What is your name?").then(function(name) {
prompter("Your age?").then(function(age) {
console.log("Hello " + name + " (" + age + ")");
});
});
}
// this one uses the nfcall funcitonality to directly call the
// promptly.prompt function (and waits for a callback).
function start2() {
Q.nfcall(promptly.prompt, "What is your name? ")
.then(function(name) {
Q.nfcall(promptly.prompt, "What is your age? ")
.done(function(age) {
console.log("Hello " + name + " (" + age + ")");
});
});
}
//start1();
I feel like the answers here can be added to for anyone seeking alternatives to solving the general problem of getting command line user input from node.
Firstly, I personally feel there is merit to moving towards the ES6 Promises API. Although not natively available yet in Node, there is a great polyfill: https://github.com/jakearchibald/es6-promise.
Secondly, I have come to like an alternative user prompting module: https://github.com/flatiron/prompt
Now assuming there exist methods 'addUserToDb', 'printUser' and 'printError' that in turn return promises, the following example is possible:
var prompt = require('node-prompt');
var Promise = require('es6-promise').Promise;
var promptUser = function(schema) {
return new Promise(resolve, reject) {
prompt.get(schema, function(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
});
};
};
promptUser(["name", "password"])
.then(addUserToDb)
.then(printUser)
.catch(printError)
I've written many 'scripts' now using this method and have found it very nice to work with and easy to maintain / adapt.

Retrieve stdout to variable

I’m trying to run child process in next code:
run = function (cmd, callback) {
var spawn = require('child_process').spawn;
var command = spawn(cmd);
var result = '';
command.stdout.on('data', function (data) {
result += data.toString();
});
command.on('exit', function () {
callback(result);
});
}
execQuery = function (cmd) {
var result = {
errnum: 0,
error: 'No errors.',
body: ''
};
run(cmd, function (message) {
result.body = message;
console.log(message);
});
return result;
}
After execution execQuery('ls') result.body is always empty, but console.log is contain value.
I ran a quick test and the command's exit event is firing before all of stdouts data is drained. I at least got the output captured and printed if I changed your exit handler to look for command.stdout's end event.
command.stdout.on('end', function () {
callback(result);
});
That should help a bit. Note there are existing libraries you might want to use for this and a truly correct implementation would be significantly more involved than what you have, but my change should address your current roadblock problem.
Random tip: it is the node convention to always reserve the first argument of callback functions for an error and your snippet is inconsistent with that convention. You probably should adjust to match the convention.
Oh sorry, let me address your question about result.body. The run function is ASYNCHRONOUS! That means that your return result; line of code executes BEFORE the run callback body where result.body = message; is. You can't use return values like that anywhere in node when you have I/O involved. You have to use a callback.

Resources