noflo network emits start and end events multiple times - noflo

I have a simple graph below. It is a math/Add component with two numeric inputs which has its output connected to a component that just prints what it receives.
I am new to noflo and I expected to see the start and end events just once, but instead I see them twice. This happens because the sockets get connected and disconnected more than once.
Why do I see more than one start/end? Is this by design?
'use strict';
var noflo = require('noflo');
var graph = noflo.graph.createGraph('test');
graph.addNode('Add', 'math/Add');
graph.addNode('Capture', 'noflotest/CaptureValue');
graph.addEdge('Add', 'sum', 'Capture', 'in');
graph.addInitial(4, 'Add', 'addend');
graph.addInitial(3, 'Add', 'augend');
noflo.createNetwork(graph, function (network) {
network.on('start', function (info) {
console.log('network started', info);
});
network.on('end', function (info) {
console.log('network stopped', info);
});
});

Good find, #akonsu. An issue has been created: https://github.com/noflo/noflo/issues/221

Related

Listen for changes in redis list

I want to write a function that constantly listens for changes in a redis list (usually when elements are pushed to the list) and pops its elements whenever the list is not empty. Then it will execute a function on the popped elements. How should I implement this?
You can use notify-keyspace-events for that
example with Node.js but the idea is similar for other languages.
const Redis = require('ioredis')
const redis = new Redis()
;(async function () {
redis.on('ready', () => {
console.log('ready');
redis.config('set', 'notify-keyspace-events', 'KEl')
// KEl => see https://redis.io/topics/notifications to understand the configuration
// l is meant we are interested in list event
redis.psubscribe(['__key*__:*'])
redis.on('pmessage', function(pattern, channel, message) {
console.log('got %s', message);
});
})
})()
Example output

redux-observable return multiple action types

I am using redux-observable and this is what I am trying to do.
When an actiontype of 'APPLY_SHOPPING_LIST' comes in dispatch 'APPLYING_SHOPPING_LIST' and after 5 seconds dispatch 'APPLIED_SHOPPING_LIST'. This is the code that I have come up with so far
const applyingShoppingListSource = action$.ofType('APPLY_SHOPPING_LISTS').mapTo({ type: 'APPLYING_SHOPPING_LISTS' });
const applyingShoppingListSourceOther = action$.ofType('APPLY_SHOPPING_LISTS').mapTo({ type: 'APPLIED_SHOPPING_LISTS' }).delay(5000);
const concatList = applyingShoppingListSource.concat(applyingShoppingListSourceOther);
return concatList;
Now the problem is that only 'APPLYING_SHOPPING_LISTS' gets fired, the 'APPLIED_SHOPPING_LISTS' does not get fired to the reducer at all. Am I missing something here?
Just to add to this, when I used flatMap it worked, given below is the code
return action$.ofType('APPLY_SHOPPING_LISTS')
.flatMap(() => Observable.concat(Observable.of({ type: 'APPLYING_SHOPPING_LISTS' }), Observable.of({ type: 'APPLYING_SHOPPING_LISTS' });
I am confused how this works and the other does not?
There's a couple issues. Since Observables are lazy, your second action$.ofType('APPLY_SHOPPING_LISTS') for applyingShoppingListSourceOther is being concat'd after the first applyingShoppingListSource, so it won't be listening for APPLY_SHOPPING_LISTS until after the first one is has completed, but it will never realistically complete because you're taking all actions that match, forever.
Said another way, your code does this:
Start listening for APPLY_SHOPPING_LISTS and when received map it to APPLYING_SHOPPING_LISTS
When that first Observable completes (it never does) start listening for APPLY_SHOPPING_LISTS again, this time when received map it to APPLIED_SHOPPING_LISTS but wait 5000 ms before emitting it.
You could solve the particular issue of the first not ever completing by using .take(1) or .first() (same thing), but you usually need to write your epics to not ever stop listening so they respond to actions at any time.
I think what you want is this:
const exampleEpic = action$ =>
action$.ofType('APPLY_SHOPPING_LISTS')
.mergeMap(() =>
Observable.of({ type: 'APPLYING_SHOPPING_LISTS' })
.concat(
Observable.of({ type: 'APPLIED_SHOPPING_LISTS' })
.delay(5000)
)
);
I used mergeMap but you may want to use switchMap to cancel any previously pending APPLIED_SHOPPING_LISTS that haven't emitted yet. Your call.

Unable to find element and send keys

So just a brief overview, I'm unable to send keys to a edit text field for android. I've successfully sent keys to this element via browser but in order to test the mobile application fully, I'd like to run e2e tests on a device using Appium.
I've successfully got Appium to click button elements but am having a hard time getting it to send keys to an edit field element.
Am I able to find elements by model when testing with android as I have set in my forgot-pin-page.js?
pin-reset-page.js
var pinResetPage = function() {
describe('The Reset Pin Flow', function () {
forgotPinPage = forgotPinPageBuilder.getForgotPinPage(),
describe('The Forgot Pin Page', function () {
it('should allow the user to enter their MSISDN and continue',
function () {
forgotPinPage.enterMsisdn('123123123');
forgotPinPage.doForgotPin();
expect(securityPage.isOnSecurityPage()).toBe(true);
});
});
}
forgot-pin-page.js
'use strict';
var ForgotPin = function () {
var forgotPinPageContent = element(by.id('forgot')),
msisdnInput = element(by.model('data.msisdn')),
return {
enterMsisdn: function (msisdn) {
return msisdnInput.sendKeys(msisdn);
}
};
module.exports.getForgotPinPage = function () {
return new ForgotPin();
};
The error i'm getting is
? should allow the user to enter their MSISDN and continue
- Error: Timeout - Async callback was not invoked within timeout spe
cified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Not sure if this is the correct solution but it worked for me. I downgraded jasmine2 to jasmine and that seemed to resolved the async timeouts I was having.

backbone view in router lost after creation

When I try to associate my router's public variable this.currentView to a newly created view, the view gets lost, the public variable is null instead of containing the newly created view.
var self=this;
var watchListsCollection = new WatchlistCollection;
watchListsCollection.url = "watchlists";
user.fetch().done(function() {
watchListsCollection.fetch().done(function () {
loggedUser.fetch().done(function () {
self.currentView = new UserView(user, watchListsCollection,loggedUser);
});
});
});
alert(this.currentView); //null
The fetch() calls you do are firing asynchronous AJAX requests, meaning the code in your done handlers are not going to be executed untill the server calls return. Once you've executed user.fetch() the browser will fire off a request and then continue running your program and alert this.currentView without waiting for the requests to finish.
The sequence of events is basically going to be
call user.fetch()
alert this.currentView
call watchListsCollection.fetch()
call loggedUser.fetch()
set the value of self.currentView
You will not be able to see the value of your currentView before the last server request have completed.
If you change your code to
var self=this;
var watchListsCollection = new WatchlistCollection;
watchListsCollection.url = "watchlists";
user.fetch().done(function() {
watchListsCollection.fetch().done(function () {
loggedUser.fetch().done(function () {
self.currentView = new UserView(user, watchListsCollection,loggedUser);
alertCurrentView();
});
});
});
function alertCurrentView() {
alert(this.currentView);
}
You should see the correct value displayed. Now, depending on what you intend to use your this.currentView for that might or might not let you fix whatever issue you have, but there's no way you're not going to have to wait for all the requests to complete before it's available. If you need to do something with it straight away you should create your UserView immediately and move the fetch() calls into that view's initialize().
fetch() is asynchronous, but you check your variable right after you've started your task. Probably these tasks, as they supposed to be just reads, should be run in parallel. And forget making a copy of this, try _.bind instead according to the Airbnb styleguide: https://github.com/airbnb/javascript
var tasks = [];
tasks.push(user.fetch());
tasks.push(watchListsCollection.fetch());
tasks.push(loggedUser.fetch());
Promise.all(tasks).then(_.bind(function() {
this.currentView = new UserView(user, watchListsCollection, loggedUser);
}, this));
or using ES6 generators:
function* () {
var tasks = [];
tasks.push(user.fetch());
tasks.push(watchListsCollection.fetch());
tasks.push(loggedUser.fetch());
yield Promise.all(tasks);
this.currentView = new UserView(user, watchListsCollection, loggedUser);
}

JavaScript variable scope issue when passing data

I am fairly new to JavaScript, and I have been having issue with the scope of what I think is a global variable.
Here is where I receive the data from the user via Socket.IO: (server.js)
var locationData = {lat: '-20', long: '20'};
var latNumber;
var longNumber;
//SocketIO data
var chat = require('express')();
var http = require('http').Server(chat);
var io = require('socket.io')(http);
io.sockets.on('connection', function (socket) {
socket.on('new message', function (msg) {
locationData = msg;
latNumber = parseFloat(locationData.lat);
longNumber = parseFloat(locationData.long);
io.emit('message', msg.message);
});
});
http.listen(5670, function(){
console.log('listening on *:5670');
});
io.on('connection', function(socket){
console.log('a user connected');
socket.on('disconnect', function(){
console.log('user disconnected');
});
});
function getLat(){
return latNumber;
}
function getLong(){
return latNumber;
}
Here is the HTML script where I want to receive that data: (HelloWorld.html)
<script type="text/javascript" src="/server.js"></script>
<script>
var viewer = new Cesium.Viewer('cesiumContainer');
var citizensBankPark = viewer.entities.add({
name : 'Test',
position : Cesium.Cartesian3.fromDegrees(getLat(),getLong()),
point : {
pixelSize : 5,
color : Cesium.Color.RED,
outlineColor : Cesium.Color.WHITE,
outlineWidth : 2
},
label : {
text : 'Test',
font : '14pt monospace',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth : 2,
verticalOrigin : Cesium.VerticalOrigin.BOTTOM,
pixelOffset : new Cesium.Cartesian2(0, -9)
}
});
viewer.zoomTo(viewer.entities);
</script>
I am trying to read the data from latNumber and longNumber defined and set in server.js into the position assignment in HelloWorld.html. The problem I am having is that once the data gets to HelloWorld.html it is undefined, and gives an error in the browser.
Now what I am not sure about is why is it undefined. Logging the latNumber and longNumber variables in server.js work fine and have data in them. Also changing the return statements in the getLat() getLong() to a hardcoded variable such as 15 works fine, and passes it onto HelloWorld.html, where it then does what it is supposed to do. It only errors out when I combine the two together!
Any help or pointers would be much appreciated! Thanks!
You've got quite a mix of issues here. Your server.js file doesn't look like client-side code at all, it looks like it's supposed to be running the server via Node.js. Are you using Node.js to run server.js to listen on port 5670, as this code suggests?
If so, good, but get rid of the <script src="/server.js"> reference on the client code. You don't want the client reading the server's source code (and ideally it shouldn't even be served, but that's a different topic).
So now you have a new problem: Your server has getLat and getLon functions that no longer exist at all on the client. You need to transmit the information from server to client.
In server.js, you can add some code to make these numbers available via REST:
chat.get('/location', function(req, res, next) {
var response = {
lat: latNumber,
lon: lonNumber
};
res.type('application/json');
res.send(JSON.stringify(response));
});
This server code will listen for requests for the URL /location and respond with a JSON string containing the numbers.
Next, your client code should request these values from the server, asynchronously (meaning we don't lock up the browser's UI while waiting for the server to respond). This is done like so:
var viewer = new Cesium.Viewer('cesiumContainer');
Cesium.loadJson('/location').then(function(data) {
viewer.entities.add({
name : 'Test',
position : Cesium.Cartesian3.fromDegrees(data.lon, data.lat),
point : {
pixelSize : 5,
color : Cesium.Color.RED,
outlineColor : Cesium.Color.WHITE,
outlineWidth : 2
},
label : {
text : 'Test',
font : '14pt monospace',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth : 2,
verticalOrigin : Cesium.VerticalOrigin.BOTTOM,
pixelOffset : new Cesium.Cartesian2(0, -9)
}
});
viewer.zoomTo(viewer.entities);
});
In the above code, loadJson returns a promise to get the data, and we add a callback function to execute when the promise resolves. The callback receives the server's data as a parameter, and builds a new entity. You can see the entity's position is based on data received from the server.
If the server's idea of the location changes, you may need to have the client periodically poll for the new location, or you can use a number of different technologies (long-polling, server-side push, EventSource, WebSockets, etc.) to get the client to update. But for starters, just reload the client page to see the new location.