callbacks in browser after socket.io emit - javascript

i am developing a way to get callbacks in the browser page, following a emit to the socketio server.
server code:
/** exec php file and retrieve the stdout */
socket.on('php', function(func, uid, data) {
console.log('php'.green + ' act:' + func + ' uid:' + uid);
runner.exec("php " + php_[func].exec,
function(err, stdout, stderr) {
if (err == null) {
socket.emit('php', {uid: uid, o: stdout});
console.log('emitted');
} else {
console.log('err '.red + stdout + ' ' + stderr);
}
});
});
this code executes a php page and retrieves the output to display or parse in the browser
it receives an id to echo back to the page, so I can know what function to execute
browser code to execute callbacks:
function log(text) {
$('.out').append(text + '<br />');
}
window.callbacks = [];
function setcb(c) {
var max = 0;
$.each(window.callbacks, function(index) {max = (index > max ? index : max);});
window.callbacks[max+1] = c;
return max+1;
};
function C(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]}
var s = io.connect("http://"+ window.location.host +":8088");
//s.emit('debug', 'here');
s.on('getid', function(){
console.log('getid cookieis: ' + C('igr') );
s.emit('setid', C('igr'));
});
s.emit('php', 'test',
setcb(
function () {
var data = JSON.parse(this);
log('callback passed' + this);
}
), null
);
s.on('php', function(data) {
//window.callbacks[j.uid].call(j.o);
log('rec' + JSON.stringify(data));
//var jsn = JSON.parse(data);
console.log(data);
console.log(window.callbacks[data.uid]);
window.callbacks[data.uid].call(data.o);
delete window.callbacks[data.uid];
window.callbacks.splice(data.uid, 1);
console.log(window.callbacks);
});
this is working, but when I try to make two requests at the same time, it doesn't run like expected, leaving one callback to execute and in the callbacks array.
test code:
s.emit('php', 'test',
setcb(
function (data) {log('callback passed' + this);}
), null
);
s.emit('php', 'test',
setcb(
function (data) {log('callback passed' + this);}
), null
);
I want to eliminate this error, and for each event received, execute the callback I define.

This is way more simple than I've imagined
You can pass by reference the callback.
server side code:
/** exec php file and retrieve the stdout */
socket.on('php', function(func, data, callback) {
console.log('php'.green + ' act:' + func);
runner.exec("php " + php_[func].exec,
function(err, stdout, stderr) {
if (err == null) {
callback(stdout);
//socket.emit('php', {uid: uid, o: stdout});
console.log('emitted');
} else {
console.log('err '.red + stdout + ' ' + stderr);
}
});
});
client side code:
function log(text) {
$('.out').append(text + '<br />');
}
function C(k){return(document.cookie.match('(^|; )'+k+'=([^;]*)')||0)[2]}
var s = io.connect("http://"+ window.location.host +":8088");
//s.emit('debug', 'here');
s.on('getid', function(){
console.log('getid cookieis: ' + C('igr') );
s.emit('setid', C('igr'));
});
s.emit('php', 'test', null,
function(data) { log('d: ' + JSON.stringify(data)); }
);

Related

Making sync call using callbacks/promises/other inside for loop in Javascript

I've a following situation:
I've an appendToFile() function that I wrote:
function appendToFile(text, file) {
(function (e) {
freeze();
FileUtils.readAsText(file)
.done(function (data) {
console.log("File data: " + data);
console.log("File text: " + e);
var text = data + e;
FileUtils.writeText(file, text, true)
.done(function () {
console.log("File saved. Text: " + text);
unfreeze();
//window.alert("File saved. Text: " + text);
})
.fail(function (err) {
console.error(err);
window.alert(err);
});
})
.fail(function (err) {
console.error(err);
});
})(text);
}
This function needs to append some text to a file. It is called from this function:
function processAttrs(clazz) {
//window.alert("Hello" + clazz.name + " " + clazz.length);
var file = FileSystem.getFileForPath("/Users/XXX/" + clazz.name + ".txt");
createFile(file, function () {
for (var i=0; i<clazz.attributes.length; i++) {
var attr = clazz.attributes[i];
var text = "private " + attr.type + " " + attr.name + "\n";
appendToFile(text, file);
}
});
}
The problem is that FileUtils.readAsText(file) is called asynchronously, so some lines that need to be written to a file get lost.
freeze() and unfreeze() are just empty functions where I thought about implementing something to stop and resume the code execution, but this mechanism doesn't seem to exist in Javascript.
I thought about using a callback as I did with createFile(file) function. The problem is that I don't know what to pass in as a callback function since the code execution is inside the for loop.
I could think of the solution without the for loop, and handling the flow manually using callbacks, but I don't like it. There have to be a more elegant solution
There is couple approaches to solve this problem. You can use async/await
so you can "block" your loop
function appendToFile(text, file) {
return new Promise((resolve, reject) => {
(function (e) {
//...
})(text);
});
}
async function processAttrs(clazz) {
//window.alert("Hello" + clazz.name + " " + clazz.length);
var file = FileSystem.getFileForPath("/Users/XXX/" + clazz.name + ".txt");
createFile(file, async function () {
for (var i=0; i<clazz.attributes.length; i++) {
var attr = clazz.attributes[i];
var text = "private " + attr.type + " " + attr.name + "\n";
const result = await appendToFile(text, file);
}
});
}
Or some kind of promise waterfall like shown here Is Node.js native Promise.all processing in parallel or sequentially?
Change your code and use the async/await syntax, like this.
async function appendToFile(text, file) {
try {
freeze();
var data = await FileUtils.readAsText(file)
console.log("File data: " + data);
console.log("File text: " + text);
var text = data + text;
await FileUtils.writeText(file, text, true)
console.log("File saved. Text: " + text);
unfreeze();
return;
} catch (e) {
console.log('Error', e)
}
}
function processAttrs(clazz) {
var file = FileSystem.getFileForPath("/Users/XXX/" + clazz.name + ".txt");
createFile(file, async function () {
for (var attr of clazz.attributes) {
var text = "private " + attr.type + " " + attr.name + "\n";
await appendToFile(text, file) //This will stop the loop until each file is completed
}
})
}

Unit testing a function and simulating an event emitter response

I am currently writing some tests against the code shown below in node.js using mocha. I want to simulate the response from the event emitter 'check_user'. I have sinon.js but I can't get my mind in the right place to work out the best way to simulate the response.
Anyone able to offer some advice on how to go about this?
TgCustomCommand.contact = new Command("contact", "Will send you a users contact card to add to your contact list. Usage is .contact nick", function (input, callback) {
var payload = input;
//Switch our contact to payload.to as this is what our checkuser function looks at
//Check the command over
if (payload.command_args === false) {
payload.response = 'msg ' + payload.to + ' ' + this.description;
return callback(null, payload);
} else {
payload.return = payload.to;
payload.to = payload.command_args; //Set up ready for nick check
//Check the nick exists
emitter.emit('check_user', payload, function (err, result) {
if (err) return callback(err, null);
payload = input; //Reset our payload so we have correct payload.to
//Check how many users we returned
if (result.length === 0) { //Not in our contact list
payload.response = 'msg ' + payload.return + ' I do not have that person in my contact list!';
return callback(null, payload);
} else if (result.length === 1) {
payload.to = result[0].Nick;
payload.response = "send_contact " + payload.return + ' ' + result[0].Phone + ' ' + result[0].Nick + " _";
return callback(null, payload);
}
else {
//loop through our object and create a list of those returned
payload.response = "msg " + payload.return + " I know multiple people with a similar nick: ";
for (var i = 0; i < result.length; i++) {
log.debug(result[i].Nick);
payload.response = payload.response + result[i].Nick + " ";
}
return callback(null, payload);
}
});
}
;
});

How to return a value from WinJS

I am developing an app which consumes a WebService like this
function callWS(filebytes, fpath, filename) { //consumes the webservice
var response;
var data = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.myCompany.com">\n' +
' <soapenv:Header/>\n' +
' <soapenv:Body>\n' +
' <ws:uploadFileService>\n' +
' <ws:filebytes>' + filebytes + '</ws:filebytes>\n' +
' <ws:fpath>' + fpath + '</ws:fpath>\n' +
' <ws:filename>' + filename + '</ws:filename>\n' +
' </ws:uploadFileService>\n' +
' </soapenv:Body>\n' +
'</soapenv:Envelope>\n';
console.log("XML SOAP: " + data + "\r\n");
var options = {
url: "http://XXX.XXX.XX.XXX:XXXX/FILESERVERWS/services/FILESERVERWS?wsdl",
type: "post",
headers: {
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": "uploadFileService"
},
data: data
};
WinJS.Promise.timeout(8000, WinJS.xhr(options)).then(
function (request) {
var doc = request.responseXML.documentElement;
var output = doc.getElementsByTagName("uploadFileServiceReturn");
//Windows.UI.Popups.MessageDialog(output[0].textContent, "the XML message").showAsync();
console.log("the XML message: " + output[0].textContent + "\r\n");
result.style.backgroundColor = "#00A000";
response = true;
},
function (error) {
Windows.UI.Popups.MessageDialog(error.status + " : " + error.statusText, "Status").showAsync();
result.style.backgroundColor = "#FF0000";
response = false;
},
function (progress) {
result.innerText = "Ready state is " + progress.readyState;
result.style.backgroundColor = "#0000A0";
}
);
return response;
}
the purpose is to consume the webService and returns a value
on success response = true
on error response = false
because I want to take an action depending if the webService returned a value by doing this
if (callWS(document.getElementById("formfield" + i).value, UID_KEY[7], arrayCaptures[i - 1].name)) {
console.log("take action a");
}
else {
console.log("take action b");
}
but it always take action B even if the webService is consumed and I get answer from the webservice, what am I doing wrong???
You'll need to return a Promise object from your function, allowing the calling script to use a then() or done() call on it to get the result. You can read more about asynchronous programming in WinJS on the msdn site, but generally it looks like this:
function callWS(filebytes, fpath, filename) {
return new WinJS.Promise(function (complete, error) {
// put your functionality here...
WinJS.Promise.timeout(8000, WinJS.xhr(options)).then(
function (request) {
// more processing...
complete(true); // or false or a variable...
},
function (e) {
// error handling unique to this function
complete(false); // OR you could just call error(e);
},
...
);
});
}
And you would use it like this:
callWS( ... ).then(
function(response) {
// handle response...
// will be either true or false
},
function(err) {
// handle errors
}
);

Simple if statement not working in NodeJS

I wrote a NodeJS app that uses eBay API to get listings from eBay. I'm having an issue where certain items are passing through even though they are supposed to be filtered out with a simple if statement.
The app receives post data from the front end as JSON, executes each search and then filters items out based on certain params. Here is the offending code:
if ( items[i].listingInfo.listingType != 'Auction' ) {
//console.log( items[i].listingInfo.listingType );
if ( items[i].primaryCategory.categoryId == '9355' ) {
//console.log( items[i].primaryCategory.categoryId );
if ( price < maxPrice && price > 40 ) {
//console.log( price, maxPrice );
file = path +
items[i].itemId + '-' +
price + '-' + maxPrice + '-' +
items[i].primaryCategory.categoryId + '-' +
items[i].listingInfo.listingType;
if ( !fs.existsSync( file ) ) {
console.log(
'File ' + file + ' does not exist.',
!fs.existsSync( file ),
items[i].listingInfo.listingType,
price < maxPrice,
items[i].itemId
);
fs.writeFile( file, ' ', function(err) {
if (err) {
if (debug)
console.log('Writing ' + file + ' failed.');
}
else {
if (debug)
console.log('Writing ' + file + ' worked.');
returnData.success = true;
returnData.results[ result.itemId ] = result;
console.log( price, maxPrice, !fs.existsSync( file ) );
console.log('success');
}
})
}
else {
returnData.discard.file[ result.itemId ] = result;
delete returnData.results[ result.itemId ];
}
}
else {
returnData.discard.price[ result.itemId ] = result;
if (debug)
console.log('FAILED (price): ' + items[i].itemId + ' is ' + ( price - maxPrice ) + ' greater than maxPrice.');
}
}
else {
returnData.discard.cat[ result.itemId ] = result;
if (debug)
console.log('FAILED (categoryId): ' + items[i].itemId + ' is ' + items[i].primaryCategory.categoryId);
}
}
else {
returnData.discard.type[ result.itemId ] = result;
if (debug)
console.log('FAILED (listingType): ' + items[i].itemId + ' is a ' + items[i].listingInfo.listingType);
}
You can see this line if ( price < maxPrice && price > 40 ) should filter out any items that are greater than the maxPrice and lower than 40. However, it does not do this. I have no idea why it's happening and what is going on here. It seems very simple and straightforward but isn't. Here is the returned object where you can see that it's not working properly.
111004318957:
listingType: "FixedPrice"
maxPrice: 170
price: 349
I'm also using node clusters, so my server.js file has this:
function start(route, handle) {
if ( cluster.isMaster ) {
for ( var i = 0; i < numCPUs; i++ ) {
cluster.fork();
}
cluster.on('exit', function( worker, code, signal) {
console.log( 'worker ' + worker.process.pid + ' died' );
})
}
else {
function onRequest(request, response) {
var postData = "";
var pathname = url.parse(request.url).pathname;
request.setEncoding("utf8");
request.addListener("data", function(postDataChunk) {
postData += postDataChunk;
});
request.addListener("end", function() {
//console.log('Request ended.');
if ( postData != '' ) {
postData = JSON.parse(postData);
}
//console.log(postData.search.searches[0]);
route(handle, pathname, response, postData);
});
}
http.createServer(onRequest).listen(8888);
console.log("Server has started.");
}
}
Any help here is appreciated, thanks.
EDIT: I should have explained that the 111004318957 is the itemId that is returned by eBay. The result object looks like this:
results: {
itemId1: {
listingType: '',
maxPrice: '',
price: ''
},
itemId2: {
listingType: '',
maxPrice: '',
price: ''
}
}
EDIT 2: price is set before this code snippet. It's returned in eBay's response and it's location is dependent on items[i].listingInfo.listingType, so there's a simple if/else to set that.
if ( items[i].listingInfo.listingType == 'AuctionWithBIN' ) {
price = parseInt( items[i].listingInfo.buyItNowPrice.USD );
}
else {
price = parseInt( items[i].sellingStatus.currentPrice.USD );
}
JSON returns listingType, maxPrice, price.
Try if (items[i].price < maxPrice && items[i].price > 40)
The author will almost certainly not be able to contribute anything to this question, to clarify if my statement is true or not, as it was asked six years ago.
However, it is fairly certain that the problem has to do with the following part of the code:
fs.writeFile( file, ' ', function(err) {
if (err) {
if (debug)
console.log('Writing ' + file + ' failed.');
}
else {
if (debug)
console.log('Writing ' + file + ' worked.');
returnData.success = true;
returnData.results[ result.itemId ] = result;
console.log( price, maxPrice, !fs.existsSync( file ) );
console.log('success');
}
})
fs.writeFile is async, and if the OP is looping over a list of results, then the result in returnData.results[ result.itemId ] = result will always refer to the last element that loop, no matter if that element matches the condition if ( price < maxPrice && price > 40 ) { or not.

How to debug page loading errors in phantomJs

Is there a way to debug page.open method of phantomjs ? My application loads some files saved locally but unfortunately the only info one can get when opening the page is if it was loaded successfully or not. What more interesting the very same page loads properly when opened in the browser.
Here's my code :
var system = require('system'),
page = require('webpage').create();
var openPage = function () {
var url = 'http:\\localhost:53794/file.html';
page.open(url, function (status) {
if (status !== 'success') {
console.log("FAIL:" + url);
phantom.exit(2);
}
var date = new Date().getTime();
var outputFilename = outputPath + 'print-' + date + '.png';
setTimeout(function () {
page.render(outputFilename);
outputArray.push(outputFilename);
setTimeout(function () {
phantom.exit(1);
}, 1);
}, 1);
});
}
openPage();
via: http://newspaint.wordpress.com/2013/04/25/getting-to-the-bottom-of-why-a-phantomjs-page-load-fails/
After creating the page variable, but before calling page.open() add the following code:
page.onResourceError = function(resourceError) {
page.reason = resourceError.errorString;
page.reason_url = resourceError.url;
};
Now you can print out the reason for a problem in your page.open() callback, e.g.:
var page = require('webpage').create();
page.onResourceError = function(resourceError) {
page.reason = resourceError.errorString;
page.reason_url = resourceError.url;
};
page.open(
"http://www.nosuchdomain/",
function (status) {
if ( status !== 'success' ) {
console.log(
"Error opening url \"" + page.reason_url
+ "\": " + page.reason
);
phantom.exit( 1 );
} else {
console.log( "Successful page open!" );
phantom.exit( 0 );
}
}
);
Debugging Function
If you read further down the blog, he has some more suggested event handlers to add. I adapted them into a function that you can use to inject the event handlers into your page object (instead of having them defined in your main code)
// this method injects some debugging event handlers
// into a PhantomJS page object.
// usage:
// var page = require('webpage').create();
// var system = require('system');
// addDebugEvents(page,system);
function addDebugEvents(page, system) {
page.onResourceError = function (resourceError) {
page.reason = resourceError.errorString;
page.reason_url = resourceError.url;
};
page.onResourceRequested = function (request) {
system.stderr.writeLine('= onResourceRequested()');
system.stderr.writeLine(' request: ' + JSON.stringify(request, undefined, 4));
};
page.onResourceReceived = function (response) {
system.stderr.writeLine('= onResourceReceived()');
system.stderr.writeLine(' id: ' + response.id + ', stage: "' + response.stage + '", response: ' + JSON.stringify(response));
};
page.onLoadStarted = function () {
system.stderr.writeLine('= onLoadStarted()');
var currentUrl = page.evaluate(function () {
return window.location.href;
});
system.stderr.writeLine(' leaving url: ' + currentUrl);
};
page.onLoadFinished = function (status) {
system.stderr.writeLine('= onLoadFinished()');
system.stderr.writeLine(' status: ' + status);
};
page.onNavigationRequested = function (url, type, willNavigate, main) {
system.stderr.writeLine('= onNavigationRequested');
system.stderr.writeLine(' destination_url: ' + url);
system.stderr.writeLine(' type (cause): ' + type);
system.stderr.writeLine(' will navigate: ' + willNavigate);
system.stderr.writeLine(' from page\'s main frame: ' + main);
};
page.onResourceError = function (resourceError) {
system.stderr.writeLine('= onResourceError()');
system.stderr.writeLine(' - unable to load url: "' + resourceError.url + '"');
system.stderr.writeLine(' - error code: ' + resourceError.errorCode + ', description: ' + resourceError.errorString);
};
page.onError = function (msg, trace) {
system.stderr.writeLine('= onError()');
var msgStack = [' ERROR: ' + msg];
if (trace) {
msgStack.push(' TRACE:');
trace.forEach(function (t) {
msgStack.push(' -> ' + t.file + ': ' + t.line + (t.function ? ' (in function "' + t.function + '")' : ''));
});
}
system.stderr.writeLine(msgStack.join('\n'));
};
}
You should change the URL
from
http:\\localhost:53794/file.html
to
http://localhost:53794/file.html

Categories