Why does socket.io-client not connect inside of an RXJS subscription? - javascript

I am trying to initiate a socket.io connection upon receiving a certain event in an rxjs observable. The socket.io connection starts fine inside startEventStream if it's outside of the subscribe, but when placed in the subscribe, even though the event is firing, the callback inside socket is never called.
function startEventStream(
stateLoaded$: Observable<LoginEvent>
): Observable<AoEvent> {
const socket = require('socket.io-client')('http://localhost:8003')
const ret = new Subject<AoEvent>()
const merged = merge(stateLoaded$, ret)
const session = '895e17a0-6c2b-11ea-8d86-45f581e4b250'
const token =
'f3ccdd81c2ece391891cba4f7d4eb8466d3d44675dd70f11e21190ae13dfdf69'
merged.subscribe({
next(val) {
process.nextTick(() => {
if (val.type == 'state-loaded') {
console.log('we should be connecting') // this prints
socket.on('connect', function() {
console.log('connected') // this doesn't print
ret.next({ type: 'socket-connected' })
socket.emit('authentication', {
session,
token
})
})
}
})
}
})
return ret
}

I got it working by passing { autoconnect: false } into the initialization of the socket and doing socket.on() inside of the subscribe. I think the connect event was getting discarded before the subscription function was called, and so the callback was never firing. Starting the connection inside solves the problem.

Related

MQTT client for Node.js functions outside the handler but not inside

I'm trying to implement a series of transport providers following a provider architecture model. Therefore, I created an abstract transport provider, which is then extended by specific providers, such as mqtt, nats, etc.
However, I'm having trouble with initializing an mqtt client. The code for initialising, connecting to and then interacting with the client, when put outside the handler, functions correctly. However, the same exact code inside a provider method connect() does not function as intended (it seems to initialize the client when watching with debugger and then skips through all the client.on functions).
This is the abstract provider, which simply outlines the methods that need to be used:
class abTransProvider {
constructor() {
this.client;
this.host;
this.username = "guest";
this.password = "guest";
if (this.constructor == abTransProvider) {
throw new Error("Abstract classes cannot be instantiated.");
};
};
init() {
throw new Error("Abstract method is not implemented");
};
connect(host) {
throw new Error("Abstract method is not implemented");
};
};
module.exports = {
abTransProvider: abTransProvider
};
This is the code for the mqtt handler itself. Note that this is just a basic skeleton, since I'm only testing the connection method now (for which there are only return values for connection and error):
const mqtt = require("mqtt");
const abTransProvider = require("../abTransProvider.js");
class specProvider extends abTransProvider.abTransProvider {
init() {
}
connect() {
this.host = "mqtt://localhost:1883";
this.client = mqtt.connect(this.host, {
username: this.username,
password: this.password,
});
// Mqtt error callback
this.client.on("error", (err) => {
console.log(err);
this.client.end();
return 1;
});
// Connection callback
this.client.on("connect", () => {
console.log(`MQTT client connected`);
return 0;
});
// MQTT subscriptions
this.client.subscribe("value", { qos: 0 });
// When a message arrives, log it
this.client.on("message", function (topic, message) {
console.log(message.toString());
});
this.client.on("close", () => {
console.log(`MQTT client disconnected`);
});
}
}
module.exports = {
specProvider: specProvider,
};
This is a simple testing program that calls the connect method:
const trProv = require("mqtt.js");
const transport = new trProv.specProvider();
const trCheck = transport.connect();
if (trCheck !== 0) {
console.log("Error when connecting to the provider")
process.exit(1)
} else {
console.log("Connection successful")
}
When the testing program calls the mqtt handler method connect(), it seems to instantiate the client but the connected flag is false. However, instead of logging an error (if there is one), the program simply skips through the client.on functions and, therefore, the return value is undefined. I would highly appreciate any feedback/comments/advice on how to resolve this! Thank you!
This code really doesn't do what you think is does.
All the return statements are in the lambda functions you have passed to the event handlers, so they will have no effect on the connect() method. They just set the return value of the lambda (which will be ignored because event callbacks don't return anything).
And as a resultconnect() ends without returning anything hence the undrfinded.
Probably the best option here is to have connect() return a promise that can be resolved in the on('connected',...) handler or rejected in the on('error',...) handler (but error can get called later so you need to be careful there).

Why closing websocket connection in Chrome by javascript is not immediately?

I tried to use rxjs webSocket to connect server. When I use .complete() function, the closingObserver is triggered immediately. But I received CloseEvent is almost 1 minute later.
So, how can I get the CloseEvent immediately?
Here is an example.
class SomeService {
private wsSource$ = webSocket({
url: "ws://xxxxxx.com/api/v1/xxx",
openObserver: this.openObserver,
closingObserver: this.closingObserver,
});
connect() {
this.wsSource$.subscribe({
next: (e) => console.log(e),
error: (err) => console.log(err),
complete: () => console.log("complete!!!")
});
}
disconnect() {
this.wsSource$.complete();
}
}
After connect(), Chrome will log the CloseEvent after almost 1 minute.
It is not logged by observer complete function, it is logged by error function.

How to cancel previous express request, so that new request gets executed?

I have this endpoint. This api takes long time to get the response.
app.get('/api/execute_script',(req,res) =>{
//code here
}
I have following endpoint which will kill the process
app.get('/api/kill-process',(req,res) => {
//code here
}
but unless first api gets response second api doesnt get executed. How to cancel previous api request and execute the second request?
You can use an EventEmitter to kill the other process, all you'll need is a session/user/process identifier.
const EventEmitter = require('events');
const emitter = new EventEmitter();
app.get('/api/execute_script', async(req,res,next) => {
const eventName = `kill-${req.user.id}`; // User/session identifier
const proc = someProcess();
const listener = () => {
// or whatever you have to kill/destroy/abort the process
proc.abort()
}
try {
emitter.once(eventName, listener);
await proc
// only respond if the process was not aborted
res.send('process done')
} catch(e) {
// Process should reject if aborted
if(e.code !== 'aborted') {
// Or whatever status code
return res.status(504).send('Timeout');
}
// process error
next(e);
} finally {
// cleanup
emitter.removeListener(eventName, listener)
}
})
app.get('/api/kill-process',(req,res) => {
//code here
const eventName = `kill-${req.user.id}`;
// .emit returns true if the emitter has listener attached
const killed = emitter.emit(eventName);
res.send({ killed })
})

socket.emit and wait until user response after that resolve promise

I am writing web app which controls hardware. I have a server communicated with the device through the serial port. Everything works except the interaction with a user. The device has registers which I repeatedly ask waiting for some values. If some values come, I emit an event to the client and confirmation box appears. The user selects resume or abort. After that client emit the response (true or false) and I would like to resolve this response in my promise function. I need to catch response from the user exactly in the function because I have a sequence of actions I need to proceed. Promise after promise. It seems that my function ends before the user answers. How to solve this problem?
this is my code on the server:
waitUserResponse(message) {
return new Promise(async (resolve) => {
const handler = function(data) {
console.log('userAnswer = ', data);
resolve(data);
return;
}
this.io.sockets.emit('alerts', message);
this.io.sockets.once('userAnswer', handler);
})
}
this is my code on the client:
componentDidMount() {
const confirmDialog = (msg) => {
return new Promise((resolve) => {
let confirmed = window.confirm(msg);
resolve(confirmed);
return;
})
}
socket.on('alerts', data => {
confirmDialog(data).then(data => {
console.log(data);
socket.emit('userAnswer', data);
});
});
}
I should use socket.id - id for my connection
io.sockets.connected[socket.id].once('userResponce', handler);

admin-on-rest how to implement a custom saga for auto-refresh

In the example provided in the aor-realtime readme
import realtimeSaga from 'aor-realtime';
const observeRequest = (fetchType, resource, params) => {
// Use your apollo client methods here or sockets or whatever else including the following very naive polling mechanism
return {
subscribe(observer) {
const intervalId = setInterval(() => {
fetchData(fetchType, resource, params)
.then(results => observer.next(results)) // New data received, notify the observer
.catch(error => observer.error(error)); // Ouch, an error occured, notify the observer
}, 5000);
const subscription = {
unsubscribe() {
// Clean up after ourselves
clearInterval(intervalId);
// Notify the saga that we cleaned up everything
observer.complete();
}
};
return subscription;
},
};
};
const customSaga = realtimeSaga(observeRequest);
fetchData function is mentioned, but it's not accessible from that scope, is it just a symbolic/abstract call ?
If I really wanted to refresh the data based on some realtime trigger how could i dispatch the data fetching command from this scope ?
You're right, it is a symbolic/abstract call. It's up to you to implement observeRequest, by initializing it with your restClient for example and calling the client methods accordingly using the fetchType, resource and params parameters.
We currently only use this saga with the aor-simple-graphql-client client

Categories