Strange behaviour coding with events - javascript

Currently working on an application that does quite a bit of database work, and thought that one smart way of getting around this should be to use events. So I set up a events emitter for the application to use, and then using this as a way to control the flow. And I kind of assumed that the events emitter would work similar to a message queue or similar.
This was not the case, and for some reason I can not get the even emitter to work as expected on the second call to the same function.
I would have understood if the problem would be that it called multiple times, but as it is, the below code will run the second function twice, but will only get passed the dbEvents.once('Ready' once. And not only can I not figure out why, it seems to work like this constantly, and will never print out "Wrote Fourth"
var mongo = require('mongodb');
var events = require('events');
var dbEvents = new events.EventEmitter();
function writeStuff(first, second, status, cuID){
console.log('Wrote '+ first);
if (status === 1) {
console.log('\t\tReady for '+ cuID);
dbEvents.emit('Ready');
} else {
console.log('\t\tReady (but not 1) for ' + cuID);
dbEvents.emit('Ready');
}
console.log('\t\tjust about to kick of number ' + status);
dbEvents.once('Ready', function (condata) {
console.log('Wrote '+ second)
dbEvents.emit('Done'+cuID)
});
}
var cuID1 = mongo.ObjectID();
writeStuff('First', 'Second', 1, cuID1);
dbEvents.on('Done'+cuID1, function(){
console.log('\tFirst completed ' + cuID1);
});
var cuID2 = mongo.ObjectID();
writeStuff('Third', 'Forth', 2, cuID2);
dbEvents.on('Done'+cuID2, function(){
console.log("\tSecond completed " + cuID2)
});
var b = 0;
for(var i = 0; i < 100000000; i++){
b++;
}
console.log("And I counted to " + b);
The output when I run the above code looks like this
Wrote First
Ready for 5540c57cd8fa9555076f9aba
just about to kick of number 1
Wrote Third
Ready (but not 1) for 5540c57cd8fa9555076f9abb
Wrote Second
First completed 5540c57cd8fa9555076f9aba
just about to kick of number 2
And I counted to 100000000
================ UPDATE ==============
Why can't I generate an event, I.E. if I use a unique identifier it does not seem like I can send it as an event.In this case I change the event to be Ready+cuID (mongodb), and now the second write never gets called.
var mongo = require('mongodb');
var events = require('events');
var dbEvents = new events.EventEmitter();
function writeStuff(first, second, status, cuID){
console.log('Wrote '+ first);
if (status === 1) {
console.log('\t\tReady for '+ cuID);
var flag = 'Ready'+cuID
dbEvents.emit(flag);
} else {
console.log('\t\tReady (but not 1) for ' + cuID);
dbEvents.emit('Ready');
}
console.log('\t\tjust about to kick of number ' + status);
dbEvents.once(flag, function (condata) {
console.log('Wrote '+ second)
dbEvents.emit('Done'+cuID)
});
}
var cuID1 = mongo.ObjectID();
writeStuff('First', 'Second', 1, cuID1);
dbEvents.on('Done'+cuID1, function(){
console.log('\tFirst completed ' + cuID1);
});
var cuID2 = mongo.ObjectID();
writeStuff('Third', 'Forth', 2, cuID2);
dbEvents.on('Done'+cuID2, function(){
console.log("\tSecond completed " + cuID2)
});
var b = 0;
for(var i = 0; i < 1000000000; i++){
b++;
}
console.log("And I counted to " + b);

It seems that you're adding the 'Ready' listener after dispatching the 'Ready' event on the first run. Now when you fire of the second run, the first listener will be called. This explains why 'Wrote Second' is logged after 'Wrote Third' is called.
Move the addition of the listener to the beginning of writeStuff().
function writeStuff(first, second, status, cuID){
console.log('\t\tjust about to kick of number ' + status);
dbEvents.once('Ready', function (condata) {
console.log('Wrote '+ second)
dbEvents.emit('Done'+cuID)
});
console.log('Wrote '+ first);
if (status === 1) {
console.log('\t\tReady for '+ cuID);
dbEvents.emit('Ready');
} else {
console.log('\t\tReady (but not 1) for ' + cuID);
dbEvents.emit('Ready');
}
}

Related

NODE.JS App gets stuck getting 2.5 million of records through and api

I've got the code that retrieves couple of millions of rows from a db and a couple of millions through an api.
let closedOrdersStartDate;
preparedOrdersPromise = tickApiConnector.obtainToken().then(function () {
return ordersController.getAllTrades(0, [], 0, accountIdsToRun);
}).then(function (trades) {
closedOrdersStartDate = new Date();
return Promise.all([trades, fthsApiConnector.getData('closed_orders', '&sort=id', 10000, 0)]);
}).then(function (tradesAndClosedOrderIds) {
//stuck before getting there
console.log('now processing orders time spent from starting getting closed_orders till now is: ' +
((new Date().getTime() - closedOrdersStartDate.getTime())/ 1000) + ' seconds');
return ordersController.processOrders(tradesAndClosedOrderIds[0], tradesAndClosedOrderIds[1]);
});
The app gets stuck after calling that getData() function.
getData: async function (entityName, getParams = '', perRequest = 10000, skip = 0) {
if(getParams.indexOf('&take=') !== -1) {
return fthsApiRequest(entityName, getParams);
}
const totalCount = await fthsApiRequest(entityName, getParams + '&getCount');
let result = [];
let count = 0;
while(count < totalCount) {
result = result.concat(await fthsApiRequest(entityName, getParams + '&take=' + perRequest + '&skip=' + count));
count += perRequest;
}
return result;
}
The function executes till the last request(I see it in logs) and after that the script gets unresponsable. I thought that it could be a memory leak and I've rewritten that getData() function in different ways but still, there's enough memory on the server and the script doesn't consume even a bit of it. Still I get 100% of CPU load in a while after that last iteration of getData() is rant. After that the app gets stuck forever.
I've tried profiling it. And there are thousands of Code move event for unknown code: 0x2a5c24bfb4c0, I'm not sure what that means, but there could be a clue in that. Here's the V8.log
The possible problem maybe in block :
while(count < totalCount) { result = result.concat(await fthsApiRequest(entityName, getParams + '&take=' + perRequest + '&skip=' + count)); count += perRequest; }
Ensure that the api give response. And on the last statement :
return result;
In async function the better use :
return Promise.resolve(result);

DOM view does not get updated, angular is not in zone

I have a zone related problem, and i can't get it working. i'm new to angular but have some basic javascript understanding.
So what is the problem, i can not get the view updated.
Im using a third party library for ionic BLE, and use this in a provider
providers/communication-controller.ts
set_ble_tolisten(){
//subscribe to receive notification, rx channel
console.log(' set_ble_tolisten' + NgZone.isInAngularZone());
this.ble.startNotification(this.peripheral_object.id,
this.TRANSPARANT_SERVICE, this.TX_CHARACTERISTIC).subscribe(
(data) => this.handlereceiveddata(data),
() => console.log('Unexpected Error', 'Failed to subscribe for
temperature changes')
)
}
ble_communication_controller(port,command,payload,payloadsize,
callback)
{
//create buffer
var BLE_Sendbuffer= new Int8Array(payloadsize+10);
var package_index = 0;
this.generatePacketID();
this.ble_send_que.push({
"packetid" : this.packetid,
"action" : callback
});
//Generate send message
BLE_Sendbuffer[ some data]
//send message
this.ble.write(this.peripheral_object.id,this.TRANSPARANT_SERVICE,
this.RX_CHARACTERISTIC,BLE_Sendbuffer.buffer).then(
() => console.log('ble message send'),
e => console.log('ble oops')
);
}
handlereceiveddata(buffer:ArrayBuffer){
var int8View = new Int8Array(buffer);
var ReceivedPacketID = 0;
console.log('handlereceiveddata ' + NgZone.isInAngularZone());
//first check the header
if( ((int8View[0] * 0x100) + int8View[1]) === this.HEADER)
{
//retrieve packetid from received packet
ReceivedPacketID = ((int8View[2] * 0x100) + int8View[3]);
if(ReceivedPacketID === 0) {
console.log('orgin io or rs232');
} else {
//find function based on packetid, and execute
let index_findresult = this.ble_send_que.findIndex(value =>
value.packetid === ReceivedPacketID);
if(index_findresult != -1)
{
this.ble_send_que[index_findresult].action(int8View);
}
//remove object from array
this.ble_send_que.splice(index_findresult, 1);
}
}
set_ble_tolisten() is called to subcribe on the promise.When data is received handlereceiveddata() is called.
To transmit data ble_communication_controller() is called, this function accepts a callback, which is stored. When the ble device response, the handlereceivedata() is called. This then call the callback which is stored a the ble_communication_controller() call
I created a page, configuration, this contains a ts a html and scss file
the configuration.ts
ionViewDidEnter() {
this.communicationController.set_ble_tolisten();
this.sync_device_gui('IO-1');
}
sync_device_gui(port){
console.log('call in zone ' + NgZone.isInAngularZone());
var io_port = 0;
if(port === ‘IO-1')
{
io_port = 0x05;
}
if(port === 'IO-2')
{
io_port = 0x06;
}
this.communicationController.ble_communication_controller(io_port,
GET_IO_STATE,0x00,0,this.update_state_gui);
}
update_state_gui(data){
//rule below added as explained in first comment (location ngzone.run).
this.zone.run(() => {
console.log('repsonse in zone ' + NgZone.isInAngularZone());
console.log('io port state ' + data);
console.log('repsonse in zone ' + NgZone.isInAngularZone());
if(data[8] == 0){
this.relay_1_toggle = true;
this.relay_2_toggle = true;
console.log('state is false set :'+ this.relay_1_toggle + '
state :' + this.relay_2_toggle );
}
if(data[8] == 1){
this.relay_1_toggle = false;
this.relay_2_toggle = false;
console.log('state is true set :'+ this.relay_1_toggle + '
state :' + this.relay_2_toggle );
}
//rule below added as explained in first comment.(location ngzone.run).
});
//ruke below added as explained in first commen. (location markForCheck())
this.cd.markForCheck();
}
when the page is loaded, ionViewDidEnter is called, the problem is that update_state_gui is called from the provider. the data receives in this function. isInAngularZone, tells me that it is false, out of zone. The
relay_1_toggle and relay_2_toggle are toggle button in html.
And the get not updated, i tried ngzone.run or a timeout and markForCheck(). but it does not work.
Thanks for your help

Array gives errors after JSON function

I'm trying to check if the twitch stream is online or offline and if so change a background colour. If i check without the array and just put in the name it works, but with the array it doesn't (I don't have a lot of knowledge of JSON).
function test() {
var twitchChannels = ["imaqtpie", "summit1g", "tyler1", "greekgodx"];
for (var i = 0; i < twitchChannels.length; i++) {
console.log(i + " " + twitchChannels[i]);
$.getJSON('https://api.twitch.tv/kraken/streams/' + twitchChannels[i] + '?client_id=XXXX', function(channel) {
console.log(i + " " + twitchChannels[i]);
if (channel["stream"] == null) {
console.log("Offline: " + twitchChannels[i])
document.getElementById(twitchChannels[i]).style.backgroundColor = "red";
} else {
console.log("Online: " + twitchChannels[i])
document.getElementById(twitchChannels[i]).style.backgroundColor = "green";
}
});
}
}
Error: http://prntscr.com/i6qj51 inside the red part is what happens inside of json fuction
Your code is quite weak since you didn't manage the callback of every get you make.
Also you didn't check if:
document.getElementById(twitchChannels[i])
is null, since the exception clearly stated that you can't get :
.style.backgroundColor
from nothing.
Basic check VanillaJS:
if(!document.getElementById("s"))
console.log('id ' + twitchChannels[i] + ' not found in dom')
else
console.log('id ' + twitchChannels[i] + ' found in dom')
Also consider mixing JQuery with VanillaJS extremely bad practice; use proper JQuery method to access dom element by ID .
You should pass twitchChannel to the function because the var i is changing, this is an issue like others already mentioned: Preserving variables inside async calls called in a loop.
The problem is that you made some ajax call in a cicle, but the ajax calls are async.
Whey you get the first response, the cicle is already completed, and i==4, that is outside the twitchChannels size: that's why you get "4 undefined" on your console.
You can change your code in such way:
function test() {
var twitchChannels = ["imaqtpie", "summit1g", "tyler1", "greekgodx"];
for (var i = 0; i < twitchChannels.length; i++) {
executeAjaxCall(twitchChannels[i]);
}
}
function executeAjaxCall(twitchChannel){
$.getJSON('https://api.twitch.tv/kraken/streams/' + twitchChannel + '?client_id=XXXX', function(channel) {
console.log(twitchChannel);
if (channel["stream"] == null) {
console.log("Offline: " + twitchChannel)
$('#'+twitchChannel).css("background-color", "red");
} else {
console.log("Online: " + twitchChannel)
$('#'+twitchChannel).css("background-color", "green");
}
});
}
}
When console.log(i + " " + twitchChannels[i]); is called inside the callback function, the variable i has already been set to 4, and accessing the 4th element of array twitchChannels gives undefined since the array has only 4 elements.
This is because $.getJSON is a shorthand Ajax function which, as the name suggests, executes your requests asynchronously. So what actually happened is, based on the output you provided,
The loop is executed 4 times, and four Ajax requests have been sent.
The loop exits; i is already set to 4 now.
The ajax requests return; the callbacks are called, but the i value they see is now 4.
You can change the console.log inside your callback to something like console.log(i + " " + twitchChannels[i] + " (inside callback)"); to see this more clearly.
The correct result can be obtained by binding the current value of i to the closure.
function test() {
var twitchChannels = ["imaqtpie", "summit1g", "tyler1", "greekgodx"];
function make_callback(index) {
return function (channel) {
console.log(index + " " + twitchChannels[index]);
if (channel["stream"] == null) {
console.log("Offline: " + twitchChannels[index])
document.getElementById(twitchChannels[index]).style.backgroundColor = "red";
} else {
console.log("Online: " + twitchChannels[index])
document.getElementById(twitchChannels[index]).style.backgroundColor = "green";
}
}
}
for (var i = 0; i < twitchChannels.length; i++) {
console.log(i + " " + twitchChannels[i]);
$.getJSON('https://api.twitch.tv/kraken/streams/' + twitchChannels[i] + '?client_id=XXXX', make_callback(i));
}
}

javascript Firebase how to fire 'value' events in sequence?

I have a function firing two 'value' Firebase events. One is necessary to get the number of children, which is corresponding to the deepest path of the next one.
function myFunction(data){
//get last child
var lastchild = 0;
var data_child = firebase.database().ref('rooms/' + 633 + '/' + 630);
data_child.once('value', function(child) {
if(child.exists()){
lastchild = child.numChildren();
console.log('function 1');
}else{
console.log('error no child');
}
});
//use this number to read the path
var msgsStored = firebase.database().ref('rooms/' + 633 + '/' + 630 + '/' + lastchild);
msgsStored.orderByChild('datetime').once('value', function(snapshot) {
var store = (snapshot.val());
snapshot.forEach(function(child) {
console.log('function 2');
//do something
}
}
}//myFunction
Firebase will always fire the last 'value' event before the first one. Why?
That will result always on variable lastchild = 0; and 'function 2' will always print before 'function 1' on console.
I tried also creating a callback(); function to try control the order with JavaScript, but will not work.
I know different events of Firebase are fired in different order, but in my case I need to read stored data only.
Anybody knows what am I missing and how to address me to a solution?
Firebase, like most of the modern web, reads data from its database asynchronously. This is easiest to see by placing some log statements in your code:
console.log("1");
var data_child = firebase.database().ref('rooms/' + 633 + '/' + 630);
data_child.once('value', function(child) {
console.log("2");
});
console.log("3");
The output of this is:
1
3
2
This is probably not what you initially expected, but explains a lot on the behavior you're seeing. When log statement 3 executes, the data from the first listener hasn't been loaded yet. So you can't make a query based on that data.
For this reason you always need to move the code that requires the data into the callback:
var lastchild = 0;
var data_child = firebase.database().ref('rooms/' + 633 + '/' + 630);
data_child.once('value', function(child) {
if(child.exists()){
lastchild = child.numChildren();
//use this number to read the path
var msgsStored = firebase.database().ref('rooms/' + 633 + '/' + 630 + '/' + lastchild);
msgsStored.orderByChild('datetime').once('value', function(snapshot) {
var store = (snapshot.val());
snapshot.forEach(function(child) {
console.log('function 2');
//do something
}
}
}else{
console.log('error no child');
}
});

onResourceReceived logs every resource twice?

I'm trying to use phantomjs to get some metrics about the likelihood of a race condition affecting a page, I have 2 script files, some functionality hosted on my site is dependant on some globals set by a file coming from a third party.
I thought that using onResourceReceived in phantomjs I could log when each file loads and then run that test a bunch of times to get an idea of how often this race condition will cause issues, an example of my code is below (it's not the actual code and I'm not affiliated with the BBC):
(function (p, wp) {
"use strict";
var page, start,
count = 0, max = 10,
webpage = require('webpage'),
url = "http://www.bbc.co.uk";
function process() {
if (max == count) {
console.log('done processing!');
p.exit();
} else {
count++;
start = new Date();
page = wp.create();
page.onResourceReceived = onResourceReceived;
page.open(url, onOpen);
}
}
function onResourceReceived(response) {
var match, t = new Date(),
url = response.url,
status = response.status;
t = t.valueOf() - start.valueOf();
if (!!(match = url.match(/locator\.css/))) {
console.log(match[0] + ': ' + t + 'msecs status: ' + status);
}
if (!!(match = url.match(/en-GB\.json/))) {
console.log(match[0] + ': ' + t + 'msecs status: ' + status);
}
};
function onOpen() {
console.log('Test ' + count + ' done!!!');
page.close();
process();
}
process();
}(phantom, require('webpage')));
This kinda runs how I expected except that each file is logged twice, why is this?
Sometimes the time differences are very different.
locator.css: 323msecs status: 200
locator.css: 323msecs status: 200
en-GB.json: 2199msecs status: 200
en-GB.json: 2200msecs status: 200
Test 1 done!!!
You need to check for response.stage property. stage will have start and end. start gives the first byte arrived time and end give you the when you got the complete response.
please add a check in your function.
function onResourceReceived(response) {
if(response.stage == 'end') return;
//rest of your code from above example.
};

Categories

Resources