I'm quite new to node and am trying to create something that gets some server info. But here's my problem. I setup a config object (this will, in time, become updated dynamically by events that occur) and then later in, in a function, I try and access a value in this object. (See code below)
So First, I setup my vars:
var util = require('util'),
child = require('child_process'),
config = {};
which works okay. Then I load my config:
function loadConfig( )
{
// Add some code for auto-loading of args
config = {
"daemons": [
["Apache", "apache2"],
["MySQL", "mysqld"],
["SSH", "sshd"]
]
};
}
and init that calling the function
loadConfig();
After that, I run my check on daemons.
function getDaemonStatus( )
{
for(var i=0; i<config.daemons.length; i++)
{
child.exec( 'ps ax -o \'%c %P\' | awk \'{if (($2 == 1) && ($1 == "\'' +
config.daemons[i][1] + '\'")) print $0}\'',
function( error, stdout, stderr )
{
console.log(config.daemons[i]);
});
}
}
The response I get is:
undefined
undefined
undefined
I don't really want to use a GLOBAL variable, so can you guys think of another way to solve my problem?
Thanks! =]
This is a gotcha that lots of people run into because of the asynchronous ordering of execution.
Your for loop will look from 0-3, and then exit when 'i' is four, obviously. The tough part to remember here is that your callback for exec won't run immediately. In only runs once the process has started, and by the time that happens, the for loop will be done.
That means that essentially, all three times that your callback function is running, you are essentially doing this:
console.log(config.daemons[4]);
That's why it prints 'undefined'.
You need to capture the 'i' value in a new scope, by wrapping the loop contents in an anonymous, self-executing function.
function getDaemonStatus( ) {
for(var i=0; i<config.daemons.length; i++) {
(function(i) {
child.exec( 'ps ax -o \'%c %P\' | awk \'{if (($2 == 1) && ($1 == "\'' +
config.daemons[i][1] + '\'")) print $0}\'',
function( error, stdout, stderr ) {
console.log(config.daemons[i]);
});
})(i);
}
}
Also, I see that your function is called 'getDaemonStatus'. Just remember that, since that exec callback is asyncronous, that also means that you can't collect the results of each callback, and then return them from the getDaemonStatus. Instead, you will need to pass a your own callback, and call the it from inside your exec callback.
Updated
Note though, the easiest way to have a scope per-iteration is to use forEach, e.g.
function getDaemonStatus( ) {
config.daemons.forEach(function(daemon, i){
child.exec( 'ps ax -o \'%c %P\' | awk \'{if (($2 == 1) && ($1 == "\'' +
daemon[1] + '\'")) print $0}\'',
function( error, stdout, stderr ) {
console.log(daemon);
});
}
}
Related
Although I usually enjoy the callback-soup that is Node.JS, I found that a certain part of my code needs to be run in a blocking manner because of an SQLite issue. (Yes, I know I could try and address the SQLite part, it actually makes more sense to ensure blocking.)
I like using the async module, and though I have a feeling that module has a feature which can be used here, I can't seem to find it. Or, maybe there is a better module out there. Anyway, without further ado:
func = function(callback) {
let i = 0;
arr.forEach(val => {
if (val.trim().length > 0) {
console.log(`Starting for user ${val}.`);
let mylc = new lcapp(val);
////// TODO this needs to be made sycnronous. /////
async.series({
getMyDetails: callback => getMyDetails(mylc, callback)
}, (err, results) => handleResults(err, results, mylc, callback));
/////////////
}
});
};
The section of code surrounded by //// I would like to block until the handleResults function returns. I understand it will require reprogramming the callback in handleResults, or maybe I need to write a parent function around func but I'd like to see if StackOverflow people have some good ideas.
You could turn it into a function that recursively calls itself when the handleResults callback is hit.
You can do this by following the example below.
fun()
function fun() {
console.time("fun")
var arr = [1, 2, 3, 4, 5]
var i = arr.length - 1;
doStuff(doStuffCallback)
function doStuffCallback() {
if (i > 0) {
i--
doStuff(doStuffCallback)
} else {
console.timeEnd("fun")
}
}
function doStuff(callback) {
setTimeout(function() {
logIt()
callback()
}, 25)
}
function logIt() {
console.log(arr[i])
}
}
// Output:
// 5
// 4
// 3
// 2
// 1
// fun: about 160ms
PS: I'm assuming you only need to be synchronous within this method and the loop therein. Other code might still be running elsewhere in your application while this runs.
Yes, I know I could try and address the SQLite part, it actually makes more sense to ensure blocking.
No it doesn't, because you can't. You need to resolve whatever issue you have with it being async because there is no way to turn asynchronous code into synchronous code.
I've written some code to convert swagger 1 documentation to swagger 2.
I point the conversion method to several resources in an array.
what I find is that it is not executing correctly and see it jump in the debugger all the way to the end of my array (which is of size 34). How do I ensure it loops through my code correctly?
for(var i = 0; i < resourcesArray.length; i++) {
Converter.convert({
from: 'swagger_1',
to: 'swagger_2',
source: 'http://example/' + resourcesArray[i]
}, function (err, converted) {
console.log(resourcesArray[i]);
// [Optional] Fill missing fields with dummy values
converted.fillMissing();
// [Optional] Validate converted spec
var fileName = resourcesArray[i] + '.json';
fs.writeFileSync(fileName, converted.stringify());
})
}
You've fallen victim to JavaScript scoping rules. Try this:
resourcesArray.forEach(function (resource) {
Converter.convert({
from: 'swagger_1',
to: 'swagger_2',
source: 'http://example/' + resource
}, function (err, converted) {
console.log(resource);
// [Optional] Fill missing fields with dummy values
converted.fillMissing();
// [Optional] Validate converted spec
var fileName = resource + '.json';
fs.writeFileSync(fileName, converted.stringify());
});
});
The problem was that by the time the asynchronous callback function (err, converted) { ... } occurs, i is equal to resourcesArray.length because the iteration is already complete. That is how JavaScript var declared variables work. Using a forEach loop ensures that the scope always contains the resource you're expecting for that operation.
Alternatively, if ES6 is okay, then you could change var to let and that would also solve the problem because let-declared variables use lexical scoping, which means that the for loop block will always contain the expected value of i even if it's used inside an asynchronous callback.
You are looping correctly, however the problem is that JavaScript does not create a closure for for body. Since you are kicking off an asynchronous operation within the loop, the value of i has changed by the time the asynchronous operation has completed and your callback has been called.
So you're going to have to create a closure immediately within the for loop, store the value you want within the closure, and then call the asynchronous operation while defining the callback within the closure as well.
for(var i = 0; i < resourcesArray.length; i++) {
(function(i) {
// Do work here with the value i
})(i);
}
This might be happening because there is an asynchronous call inside your for loop. You need to freeze the value of i, for each iteration. You can use closure() for this. In case you want to keep track of when all iterations have completed, you can keep a counter:
var counter = resourcesArray.length;
for(var i = 0; i < resourcesArray.length; i++) {
var resources = resourcesArray[i];
(function( resources, i ){
Converter.convert({
from: 'swagger_1',
to: 'swagger_2',
source: 'http://example/' + resources
}, function (err, converted) {
console.log(resources);
// [Optional] Fill missing fields with dummy values
converted.fillMissing();
// [Optional] Validate converted spec
var fileName = resources + '.json';
fs.writeFileSync(fileName, converted.stringify());
counter--;
if( counter <= 0 )
{
//next();
}
})
})(resources, i );
}//for
EDIT
Let me get more to the point. I'm trying to create a psuedo promise implementation. The idea here being that I have a callback that won't be executed until an asynchronous call is received. So I'm simply queueing up all the calls to this function until the time at which it's notified that it can be executed. The queue is emptied and any further call to the function is SUPPOSED to execute immediately, but for some reason, the function is still queueing. This is because, for whatever reason, my redefinition of the runner function is not working correctly. The code below was my sleep deprived, frustrated version of every thought that went through my head. Here's the actual code:
function Promise(callback){
var queue = []
, callback = callback
, runner = function(){
queue.push({
context: this,
args: Array.prototype.slice.call(arguments, 0)
});
}
;//var
runner.exec = function(){
for(var i = 0, ilen = queue.length; i < ilen; i++){
var q = queue[i];
callback.apply(q.context, q.args);
}
runner = callback;
};
return runner;
}
test = Promise(function(){
$('<div/>').appendTo('#output').html(Array.prototype.slice.call(arguments,0).toString());
});
test(1,2);
test(3,4);
test.exec();
test(5,6);
http://jsfiddle.net/a7gaR/
I'm banging my head against the wall with this one. I'm trying to reassign variables in a function from a call outside the function itself (ideally by passing a reassignment function as a callback). In the example I posted on jsfiddle, I made a global function that, in theory, has a reference to the variables contained within its parent function. Upon calling that external function, I expect it to reassign the values that the other function is using. It doesn't seem to work this way.
window.test = function temp() {
var val = 7,
func = function() {
return val;
};
window.change = function() {
window.test.val = 555555;
$('<div>Changing ' + val + ' to ' + window.test.val +
'</div>').appendTo($output);
val = window.test.val;
temp.val = window.test.val;
func = function() {
return 'Why isn\'t this working?';
}
}
return func();
}
var $output = $('#output');
$('<div/>').appendTo($output).html('::' + test() + '::');
window.change();
$('<div/>').appendTo($output).html('::' + test() + '::');
http://jsfiddle.net/YhyMK/
The second time you call test you're creating a new local variable called func and defining a new window.change that closes over that new variable. The changes you made to the original func by calling the original window.change are not relevant in the second call.
Also note that the following line:
window.test.val = 555555;
...does not modify/refer to the val variable in the outer function. window.test.val refers to a property named val on the test object (which happens to be a function), not any local variable.
You are trying to refer to a local variable in a function with the syntax func.varname. That won't work, that's not the way local variables work.
I finally created a function that would perform this operation. The gist for it is here: https://gist.github.com/2586972.
It works like this...
You make a call to Defer passing the callback whose functionality you'd like to delay:
var deferredCB = Defer(function(){ console.log(this,arguments) };
deferredCB will now store all of the arguments you pass allowing them to be executed at some later date:
defferedCB(1);
defferedCB(2);
Now, when you're ready to perform the operation, you simply "execute" deferredCB:
defferedCB.exec();
Which results in:
// window, 1
// window, 2
All future calls to deferredCB will be executed immediately. And now that I'm thinking about it, I'll probably do a rewrite to allow you to reset deferredCB to it's pre-executed state (storing all the arguments again).
The key to making it work was having a wrapper function. Javascript simply won't let you reassign a function while it's being executed.
TADA!!!
Is there a way to print arguments' list in full or in parts in JavaScript?
Example:
from within the function my_assert(a!=b) I'd like to print a!=b, or even 2!=3 for a particular function call.
you can't. a!=b is executed first and only the result of this (true or false) is given to your function so you don't have a chance to get back a!=b or 2!=3.
console.log (arguments)
will print the arguments given to the function, but in your case, all your function sees is a boolean, because a != b will be evaluated first, and only the result passed as a parameter in the function call.
umm... here, I'll google it for you :)
http://www.seifi.org/javascript/javascript-arguments.html
As some others pointed out, passing in a test (a != b) will only get you a boolean value (true|false) as your argument. But if you call myAssert(a,b), you can then evaluate the arguments and test their equality, as well as print their values, following the advice in the link I pasted.
Here you go:
my_assert = (test) ->
str = my_assert.caller.toString()
match = /return my_assert\((.*)\)/.exec str
console.log match[1], test
a = '1'
b = '2'
do -> my_assert(a!=b)
do -> my_assert(b!=a)
> a !== b true
> b !== a true
http://jsfiddle.net/m7qRN/
The only caveat is you have to call your my_assert calls from separate anonymous functions in order to be able to reliably get the source code of the assertion call.
In CoffeeScript its not so bad using the do -> notation.
In JS this is: (function(){my_assert(a!=b)})();.
You could pass in the test as a function like so:
my_assert -> a!=b
my_assert(function(){a!=b});
//
// A variation on vaughan's answer, from Oct 1 '13
// Includes simple unit test to debug the regex for multiline assert expressions
// Use console.error and/or 'throw new Error' depending how you handle errors
//
function test_assert() {
try {
(function () { assert(3 === 'a') })();
(function () {
assert('foo' !== 'bar')
})();
(function () {
assert('foo' ===
'bar')
})();
} catch (e) {
console.error(e.stack)
}
} // test_assert
function assert(expression) {
if (!expression) {
let caller = assert.caller.toString()
let test = caller.match(/assert[^]*\)/)
//throw new Error(test[0] + ' failed');
console.error('%s failed', test[0])
}
} // assert
/////////////////////////////////
// Here's a simple assert()
function assert2(condition, message) {
if (!condition) {
console.error('assert (%s) failed: "%s"', condition, message)
throw new Error("Assertion failed: " + message);
}
}
You can't do it. When you have the following line:
my_assert(a!=b);
The expression a!=b will first be evaluated and its result will be passed to my_assert.
Assuming your my_assert() function is used specifically for your own testing and you can control how it works and what you pass into it you could do something like this:
my_assert(a!=b, "a!=b");
I.e., pass an extra parameter to the function with a string representation of what is being tested. Obviously that doesn't stop you accidentally saying my_assert(a!=b, "a==b");, and it's clunky, but I can't think of another way to do it.
I think this issue goes beyond typical variable scope and closure stuff, or maybe I'm an idiot. Here goes anyway...
I'm creating a bunch of objects on the fly in a jQuery plugin. The object look something like this
function WedgePath(canvas){
this.targetCanvas = canvas;
this.label;
this.logLabel = function(){ console.log(this.label) }
}
the jQuery plugin looks something like this
(function($) {
$.fn.myPlugin = function() {
return $(this).each(function() {
// Create Wedge Objects
for(var i = 1; i <= 30; i++){
var newWedge = new WedgePath(canvas);
newWedge.label = "my_wedge_"+i;
globalFunction(i, newWedge]);
}
});
}
})(jQuery);
So... the plugin creates a bunch of wedgeObjects, then calls 'globalFunction' for each one, passing in the latest WedgePath instance. Global function looks like this.
function globalFunction(indicator_id, pWedge){
var targetWedge = pWedge;
targetWedge.logLabel();
}
What happens next is that the console logs each wedges label correctly. However, I need a bit more complexity inside globalFunction. So it actually looks like this...
function globalFunction(indicator_id, pWedge){
var targetWedge = pWedge;
someSql = "SELECT * FROM myTable WHERE id = ?";
dbInterface.executeSql(someSql, [indicator_id], function(transaction, result){
targetWedge.logLabel();
})
}
There's a lot going on here so i'll explain. I'm using client side database storage (WebSQL i call it). 'dbInterface' an instance of a simple javascript object I created which handles the basics of interacting with a client side database [shown at the end of this question]. the executeSql method takes up to 4 arguments
The SQL String
an optional arguments array
an optional onSuccess handler
an optional onError handler (not used in this example)
What I need to happen is: When the WebSQL query has completed, it takes some of that data and manipulates some attribute of a particular wedge. But, when I call 'logLabel' on an instance of WedgePath inside the onSuccess handler, I get the label of the very last instance of WedgePath that was created way back in the plugin code.
Now I suspect that the problem lies in the var newWedge = new WedgePath(canvas); line. So I tried pushing each newWedge into an array, which I thought would prevent that line from replacing or overwriting the WedgePath instance at every iteration...
wedgeArray = [];
// Inside the plugin...
for(var i = 1; i <= 30; i++){
var newWedge = new WedgePath(canvas);
newWedge.label = "my_wedge_"+i;
wedgeArray.push(newWedge);
}
for(var i = 0; i < wedgeArray.length; i++){
wedgeArray[i].logLabel()
}
But again, I get the last instance of WedgePath to be created.
This is driving me nuts. I apologise for the length of the question but I wanted to be as clear as possible.
END
==============================================================
Also, here's the code for dbInterface object should it be relevant.
function DatabaseInterface(db){
var DB = db;
this.sql = function(sql, arr, pSuccessHandler, pErrorHandler){
successHandler = (pSuccessHandler) ? pSuccessHandler : this.defaultSuccessHandler;
errorHandler = (pErrorHandler) ? pErrorHandler : this.defaultErrorHandler;
DB.transaction(function(tx){
if(!arr || arr.length == 0){
tx.executeSql(sql, [], successHandler, errorHandler);
}else{
tx.executeSql(sql,arr, successHandler, errorHandler)
}
});
}
// ----------------------------------------------------------------
// A Default Error Handler
// ----------------------------------------------------------------
this.defaultErrorHandler = function(transaction, error){
// error.message is a human-readable string.
// error.code is a numeric error code
console.log('WebSQL Error: '+error.message+' (Code '+error.code+')');
// Handle errors here
var we_think_this_error_is_fatal = true;
if (we_think_this_error_is_fatal) return true;
return false;
}
// ----------------------------------------------------------------
// A Default Success Handler
// This doesn't do anything except log a success message
// ----------------------------------------------------------------
this.defaultSuccessHandler = function(transaction, results)
{
console.log("WebSQL Success. Default success handler. No action taken.");
}
}
I would guess that this is due to that the client side database storage runs asynchronous as an AJAX call would. This means that it doesn't stops the call chain in order to wait for a result from the invoked method.
As a result the javascript engine completes the for-loop before running the globalFunction.
To work around this you could perform the db query inside a closure.
function getDataForIndicatorAndRegion(indicator_id, region_id, pWedge){
return function (targetWedge) {
someSql = "SELECT dataRows.status FROM dataRows WHERE indicator_id = ? AND region_id = ?";
dbInterface.sql(someSql, [indicator_id, region_id], function(transaction, result) {
targetWedge.changeColor(randomHex());
});
}(pWedge);
}
This way you preserve pWedge for each execution. Since the second method is invoking it self and send what pWedge is right now as an argument.
EDIT: Updated the code from comments. And made a change to it. The callback function maybe shouldn't be self invoked. If it invoke it self the result of the function is passed as a argument. Also if it doesn't work, try passing the other arguments.
i suspect your problem is the modifed closure going on inside globalFunction:
function(transaction, result){
targetWedge.logLabel();
})
read this