Javascript Async.js Parallel not working with socket.io - javascript

I have the following block of code:
async.parallel([
function(cb){ module.rpc("user.data",{"username":data.username},cb); },
function(cb){ module.rpc("group.list",{"username":data.username},cb); },
function(cb){ module.rpc("set.list",{},cb); }
],function(error,result){
if(error){ callback(error); return; }
var user = result[0], groups = result[1], sets = result[2];
callback(null,template.render({"user":user,"groups":groups,"sets":sets}));
});
module.rpc is a function that fetches the necessary data from the server via socket.io. Now, the final function (async.parallel's second argument) is supposed to be called only after the given 3 functions have called-back. However, during the template (EJS) rendering, when I try to access groups.data, I sometimes get the error:
Uncaught TypeError: Cannot read property 'data' of undefined
The code seems perfectly fine to me, but works only occasionally. I have reloaded the page repeatedly, without changing the underlying code, and had a success rate of about 20%. I have absolutely no clue why things are going wrong here. All I can guess is that the assignment of that variable is delayed. And so I tried delaying the rendering using window.setTimeout, but to no avail. Why is this happening? How do I fix it?

socket.io keeps sending events to the server repeatedly until it gets an acknowledgement. Depending on the server load, acknowledgements might not be immediate. So multiple identical requests were being sent, and since async.parallel uses a counter instead of individually tracking each function, the final function was getting prematurely called.

Related

Why does timer for waits start before the steps are actually executed in a protractor test? Eventloop wrangling

tl;dr: When I run my test case, steps executed seem to work, but the test bails out early on a failure to find an element that hasn't even loaded yet. It seems like the waits I have around locating certain elements are loaded/launched as soon as the test is launched, not when the lines should actually be executed in the test case. I think this is happening because the page is barely (correctly) loaded before the "search" for the element to verify the page has loaded bails out. How do I wrangle the event loop?
This is probably a promise question, which is fine, but I don't understand what's going on. How do I implement my below code to work as expected? I'm working on creating automated E2E test cases using Jasmine2 and Protractor 5.3.0 in an Angular2 web app.
describe('hardware sets', () => {
it('TC3780:My_Test', async function() {
const testLogger = new CustomLogger('TC3780');
const PROJECT_ID = '65';
// Test Setup
browser.waitForAngularEnabled(false); // due to nature of angular project, the app never leaves zones, leaving a macrotask constantly running, thus protractor's niceness with angular is not working on our web app
// Navigate via URL to planviewer page for PROJECT_ID
await planListingPage.navigateTo(PROJECT_ID); // go to listing page for particular project
await planListingPage.clickIntoFirstRowPlans(); // go to first plan on listing page
await planViewerPage.clickOnSetItem('100'); // click on item id 100 in the plan
});
});
planViewerPage.po.ts function:
clickOnSetItem(id: string) {
element(by.id(id)).click();
browser.wait(until.visibilityOf(element(by.css('app-side-bar .card .info-content'))), 30000); // verify element I want to verify is present and visible
return expect(element(by.css('app-side-bar .card .info-content')).getText).toEqual(id); //Verify values match, This line specifically is failing.
}
This is the test case so far. I need more verification, but it is mostly done. I switched to using async function and awaits instead of the typical (done) and '.then(()=>{' statement chaining because I prefer not having to do a bunch of nesting to get things to execute in the right order. I come from a java background, so this insanity of having to force things to run in the order you write them is a bit much for me sometimes. I've been pointed to information like Mozilla's on event loop, but this line just confuses me more:
whenever a function runs, it cannot be pre-empted and will run entirely before any other code
runs (and can modify data the function manipulates).
Thus, why does it seem like test case is pre-evaluated and the timer's set off before any of the pages have been clicked on/loaded? I've implemented the solution here: tell Protractor to wait for the page before executing expect pretty much verbatim and it still doesn't wait.
Bonus question: Is there a way to output the event-loop's expected event execution and timestamps? Maybe then I could understand what it's doing.
The behavior
The code in your function is running asynchronously
clickOnSetItem(id: string) {
element(by.id(id)).click().then(function(){
return browser.wait(until.visibilityOf(element(by.css('app-side-bar .card .info-content'))), 30000);
}).then(function(){
expect(element(by.css('app-side-bar .card .info-content')).getText).toEqual(id);
}).catch(function(err){
console.log('Error: ' + err);
})
}

Socket is called multiple times

I read other questions about this issue but didn't find a solution for the problem. I saw that you can removeListeners or move the socket code out of the click handler for example but it doesn't seem to fit my problem.
I am using sockets + ajax. My code logic goes as follows:
//client side
button.onclick = function () {Communicate(info);return false;};
function Communicate(info) {
// ajax call
}
// server side -> receive ajax
app.post("/...",function(req,res) {
// do stuff (...)
// send global socket call
io.sockets.emit("GlobalCall",xx);
// return ajax call to the user who triggered this
res.send(...));
});
// client side again
socket.on("GlobalCall",function(x)) {
// this is called twice sometimes for some reason... It should be called only once each time the server emits ...
}
As I commented on the code, the server emits only once but "GlobalCall" happens twice sometimes.
Any help? Thank you
The solution wasn't really related to sockets. What was happening had to do with jquery animations that were executed twice for each assigned property. I didn't know that and didn't notice it. Wrong to blame the sockets.

How to run a function when all the data loaded?

I process thousands of points asynchronously in ArcGIS JS API. In the main function, I call functions processing individual features, but I need to finalize the processing when all the features are processed. There should be an event for this, though I didn't find any and I'm afraid it even doesn't exist - it would be hard to state that the last item processed was the last of all. .ajaxStop() should do this, but I don't use jQuery, just Dojo. Closest what I found in Dojo was Fetch and its OnComplete, but as far as I know it's about fetching data from AJAX, not from other JS function.
The only workaround idea I have now is to measure how many features are to be processed and then fire when the output points array reaches desired length, but I need to count the desired number at first. But how to do it at loading? Tracking the data to the point where they are read from server would mean modifying functions I'm not supposed to even know, which is not possible.
EDIT - some of my code:
addData: function (data) {
dojo.addOnLoad(
this.allData = data,
this._myFunction()
);
},
Some comments:
data is an array of graphics
when I view data in debugger, its count is 2000, then 3000, then 4000...
without dojo.addOnLoad, the count started near zero, now it's around 2000, but still a fraction of the real number
_myFunction() processes all the 2000...3000...4000... graphics in this._allData, and returns wrong results because it needs them all to work correctly
I need to delay execution of _myFunction() until all data load, perhaps by some other event instead of dojo.addOnLoad.
Workarounds I already though of:
a) setTimeout()
This is clearly a wrong option - any magic number of miliseconds to wait for would fail to save me if the data contains too much items, and it would delay even cases of a single point in the array.
b) length-based delay
I could replace the event with something like this:
if(data.length == allDataCount) {
this._myFunction();
}
setTimeout(this._thisFunction, someDelay);
or some other implementation of the same, through a loop or a counter incremented in asynchronously called functions. Problem is how to make sure the allDataCount variable is definitive and not just the number of features leaded until now.
EDIT2: pointing to deferreds and promises by #tik27 definitely helped me, but the best I found on converting synchronous code to a deferred was this simple example. I probably misunderstood something, because it doesn't work any better than the original, synchronous code, the this.allData still can't be guaranteed to hold all the data. The loading function now looks like this:
addData: function (data) {
var deferred = new Deferred();
this._addDataSync(data, function (error, result) {
if (error) {
deferred.reject(error);
}
else {
deferred.resolve(result);
}
});
deferred.promise.then(this._myFunction());
},
_addDataSync: function (data, callback) {
callback(this.allData = data);
},
I know most use cases of deferred suppose requesting data from some server. But this is the first time where I can work with data without breaking functions I shouldn't change, so tracking the data back to the request is not an option.
addonload is to wait for the dom.
If you are waiting for a function to complete to run another function deferred/promises are what is used.
Would need more info on your program to give you more specific answers..
I sort of solved my problem, delaying the call of my layer's constructor until the map loads completely and the "onUpdateEnd" event triggers. This is probably the way how it should be properly done, so I post this as an answer and not as an edit of my question. On the other hand, I have no control over other calls of my class and I would prefer to have another line of defense against incomplete inputs, or at least a way to tell whether I should complain about incomplete data or not, so I keep the answer unaccepted and the question open for more answers.
This didn't work when I reloaded the page, but then I figured out how to properly chain event listeners together, so I now can combine "onUpdateEnd" with extent change or any other event. That's perfectly enough for my needs.

Meteor run a function after database connection

I need to read from the database and aggregate some results, and then set a number of reactive variables on the client. I can only do this after the database has been connected.
I tried to do it in onRendered,
Template.header.onRendered(function(){
recalculate();
});
It works locally, but not when deployed to meteor.com. Where should I do the function call?
The onRendered callback has nothing to do whatsoever with data over the wire, it simply tells you when the template appears on the DOM (unless you archaically wait for the data to start rendering).
It works locally because the data is very fast to arrive and can be ready before a template rendering (which takes a few milliseconds).
However on real conditions latency appears: the data takes a while to arrive.
Since you use an inappropriate way to know how your data is ready, stuff goes wrong. It would have failed just as badly if you had used any other kind of arbitrary trigger:
setTimeout(recalculate, 150);
To know when the database has connected, use the subscription's built-in ready callbacks:
var dataSub = Meteor.subscribe('my data', recalculate); //Method 1
Tracker.autorun(function(computation) {
if(dataSub.ready()) {
computation.stop();
recalculate();
}
}); //Method 2
Choose one of the two methods.
The second one is more modular since you can have multiple computations watching one subscription.

JavaScript, node.js wait for socket.on response before continuing

I need to get information from the server on the client side.
So on the server side I got this when a client first connect:
socket.on('adduser', function(username){
// misc code, where i set num_player and whatnot
socket.emit('confirmauth', socket.id, socket.num_player, function(data){
console.log(data)
});
// code
}
and on the client side I got this:
var current_player;
socket.on('confirmauth', function(id, username, num, callback) {
current_player = new Player(username,id, num);
console.log(current_player.id); // works
console.log(current_player.num); //works
callback('ok i got it');
});
console.log(current_player.id); //undefined
console.log(current_player.num); //undefined
my problem is that outside of the socket on, the player is not defined. It seems that javascript doesn't wait for my socket on to retrieve data before carrying on.
I tried to wrap socket.on in a $.when done, but it doesn't work. I tried to do a callback, but I think I may not have understood very well how it is supposed to work. So if one of you is willing to help me, I will be grateful
Thank you for your answers.
If you are putting the current_player variable outside of the on callback in an attempt to return it, the alternative is to make your own function receive a callback
function getPlayer(onDone){
socket.on('confirmauth', function(id, username, num, callback) {
var current_player = new Player(username,id, num);
onDone(current_player);
});
}
And instead of doing
var player = getPlayer();
//...
You do
getPlayer(function(player){
//...
});
It kind of sucks that the "callbackyness" is a bit infectious in Javascript but such is life until everyone starts using Generators instead.
This is since socket.on runs taking a callback, and is in the callback where the player is set. however, after calling socket.on you try to read the player, but it is not set, since the callback was not called. Remember you're dealing with asynchronous programming. Try using nibble to chain that code after the callback.
Remember: socket.on and many other socket handlers -and even many other event handles (e.g. jquery)- DON'T wait to the event to happen, so you must think asynchronously.
Take this example:
You call socket.on while the socket is connecting (The connection takes 3 seconds since, e.g., the client is behind Tor). socket.on assigns the event handler (it's the only thing it does since it works for the 11 or more events it has, in the same way).
You access the player.
But the point 1 does not take 3 seconds since it's just an assignment - the connection is what takes 3 seconds to be established. In that sense, you have an undefined (actually: unassigned) variable.
All of these operations are asynchronous, so it's best to avoid global variables and statefulness as much as possible.
That said, have your callback take the newly created Player object to where it needs to go - a register of players, maybe? - and transport it that way.
callback(current_player)
From there, you can put it into a globally available object as you like, for instance if you're using Backbone or have something on the server side keeping track of current users.

Categories