socket.emit() error handling with socket.io - javascript

I am using
$('btn').click(function() {
socket.emit('load more', function (objs) {
// do something
});
});
on the client to get objects from the server.
But sometimes the connection is lost and nothing happens. How can I make error handling such that I can tell the user that the server doesn't answer instead of letting nothing happen as it is now?
Edit
So what I want is something like
$('btn').click(function() {
var emit = socket.emit('load more', function (objs) {
// do something
});
if (!emit) {
console.log("It didn't work. Maybe you lost connection or the server is not running.");
}
});
I do know that I cannot do this, but would it be possible using promises or something?

Second argument to emit is the data that we need to pass on the event 'load more'.
If you just want to server to let know that you need data, without passing any extra info, just do socket.emit('load more'). In server, listen for event 'load more' and send data to client with particular event-name. In Client, listen for the event 'event-name' and receive data in callback like,
socket.on('event-name', function(data_from_server){
//do something
});
If you want to handle the case, where server not sends any data then wait for some time after sending the event 'load mode' and handle it.

Related

How to send broadcast data with Web Socket Server?

I need to send data from my BE to FE when an event happen, so I have created an Event Emitter and in the Fucntion i would like to send a Broadcast message to all the client (the message is an Array of Object), this is my code but unfortunately doesn't work properly.
emitter.on('readData', function(){
wss.broadcast = function(data) {
wss.clients.forEach(client => client.send(Object));
};
});
Ok instead of broadcasting, you are defining a function to broadcast.
emitter.on('readData', function(){
wss.clients.forEach(client => client.send(Object));
});
More details here.

clientside nodejs readline not triggering 'close' event except on CTRL-C from server

I am trying to make a simple server/client TCP connection with javascript using nodejs.
I have client.js code (which I can't modify) which has this line:
var s = net.connect({port: this._port}),
rl = readline.createInterface({input: s});
s.write('quit\r\n');
rl.on('close', () => done(null, output));
The problem is, I cannot get that event to trigger. On the server side, I have
socket.once("close", function () {
console.log("Connection from %s closed", remoteAddress);
// socket.destroy();
});
socket.once("end", function() {
console.log("Ending connection");
// socket.destroy();
})
The server side receives an end event, and prints it out, followed by the close. However, I can't seem to get that client to get that end event naturally. Something weird that does work though, is if I CTRL+C the server and stop it midway. That causes the client to get the 'end' and move on with the code. As of now, it just timeouts. Now I am wondering if it's possible to automatically send a CTRL+C or if there is any other way to get this 'end' to trigger.
Methods I have tried:
socket.destroy(), sending EOT character.
Again, I cannot modify this client.js.

Express node.js - Does the render function has a promise?

After rendering my index.html (which works fine), I would like to send some additional data via sockets. For that, I would need a promise for the rendering process. At the moment, the code runs synchron. The socket data is sent and moments later the data is overwritten due to the later ending rendering process. Looking for something like:
res.render('index', {title: "XYZ"})
.then(function(){
//.. do something
});
Is there a different approach? Or is the only solution to ask for the data via the client?
Thanks for any help!
Does the render function has a promise?
The documentation doesn't mention one, so presumably not.
For that, I would need a promise for the rendering process.
Not necessarily, just some kind of notification that the data had been sent. Promises are one kind of notification, but not the only kind.
The docmentation shows that render will call a callback function with the rendered HTML, so you could use that callback to send the HTML along with whatever you want to have follow it:
res.render("index", {title: "XYZ"}, function (err, html) {
if (err) {
// ...send an error response...
return;
}
res.send(html);
// ...send your other stuff here...
});
But if you want a promise, you could use util.promisify on res.render. It's a bit of a pain because promisify doesn't make handling this straightforward, so you have to use bind:
const resRender = util.promisify(res.render.bind(res));
// ...
resRender("index", {title: "XYZ"})
.then(html => {
res.send(html);
// ...send your other stuff here...
})
.catch(err => {
// ...send an error response...
});
You've said you're sending further information "via sockets." That makes it sound to me like the further information you're sending isn't being sent via the res response, but via a separate channel.
If so, and if you want to wait to send that until the response is sent, you can start your socket sending in response to the finish event on the response:
res.on("finish", () => {
// Send your socket message here
});
res.render("index", {title: "XYZ"});
(Remember that an Express Response object is an enhanced version of the Node.js ServerResponse object, which is what provides this event.)
But even then, all that means is that the data has been handed over to the OS for transmission to the client. From the documentation:
...this event is emitted when the last segment of the response headers and body have been handed off to the operating system for transmission over the network. It does not imply that the client has received anything yet.
I don't think you have anything beyond that to hook into.

Is there a way to send response from server even if no callback is provided from client side?

Issue clarification
When we use .emit() or .send() and we also want to confirm message reception (so called acknowledgements) we simply write something like this:
socket.emit('someEvent', payload, callback);
What this question is all about is a callback part. That's the great stuff as it allows to generally send back some data as a response with no extra events emitted. All that server needs to do is to handle the request in a proper way:
socket.on('someEvent', (payload, callback) => {
doSomeStuff();
callback(someData);
);
That works just fine when we deal with a success case. But what shall we do in these cases:
1) Callback was not sent from the client side / callback's not a function and there's a need to respond from the server side with something like 'Error: no callback is provided. Usage: ...'
Example:
Client side - socket.emit('someEvent'); or socket.emit('someEvent', 1);
Server side - socket.on('someEvent', callback => callback());
or
2) While handling the request something went wrong (e.g. an unsuccessful validation result) and we need to report this in a way like: 'No payload is provided or it is invalid'
Example:
Server side -
socket.emit('someEvent', payload, callback => {
checkPayload();
callback(someData);
});
Client side - socket.on('someEvent', invalidPayload, callback);
Question: is there a mechanism to create custom callback from responder's side?
My workings and workarounds
1) As for the missing callback or that one which is not a function I've concluded that I can only validate it and then invoke it only in case of its validity. So the server side is undergoing some changes:
socket.emit('someEvent', callback => callback instanceof Function && callback()); //check callback correctness
Pros: there won't be an internal error if a callback is not a function as expected.
Cons: in case of invalid callback a client won't be noticed about it.
2) As for the case when we need to send some error back I've only found a workaround to return a specific, agreed in advance, falsy value like null so that it means that no data can be returned.
socket.emit('someEvent', payload, callback => {
checkPayload();
callback(someData || null); //send falsy, error-like value instead
});
Pros: a client will be noticed about some error by getting null.
Cons: from server side there's no simple middleware function that validates the input data and returns error before the main logic is being executed.
I've thought about middlewares for reaching the needed functionality, but there's no, so to say, 'event level middlewares' yet, only on the whole namespace and socket levels. Shall I try to filter events by their names on the socket level to attach the needed functionality and send error in a way like next(new Error(...));? In this case there can be a work with error event listening, I guess.
socket.io / socket.io-client versions used: 2.3.0
1) Callback was not sent from the client side / callback's not a function and there's a need to respond from the server side with something like 'Error: no callback is provided. Usage: ...'
The client and server have to agree how to do this. If the client doesn't provide a callback, then the server argument will be undefined so you can detect that from the server.
So, the proper way to do it is this:
// client
socket.emit('someMsg', someData, function(response) {
console.log(`Got ${response} from server`);
});
// server
io.on('connection', socket => {
socket.on('someMsg', (data, fn) => {
console.log(`Got data ${data} from client, sending response`);
// if client wants a response, send the response
if (fn) {
fn("got your data");
}
});
});
So, if the client does not pass the callback, then fn on the server side will be undefined. So, you are correct to test for that before calling it.
2) As for the case when we need to send some error back I've only found a workaround to return a specific, agreed in advance, falsy value like null so that it means that no data can be returned.
Yes, you have to agree in advance how to send an error back. The cleanest way to send an error back would probably be to wrap your response in an object and use a .error property on that object.
// client
socket.emit('someMsg', someData, function(response) {
if (response.error) {
console.log(`Got error ${response.error} from server`);
} else {
console.log(`Got data ${response.data} from server`);
}
});
// server
io.on('connection', socket => {
socket.on('someMsg', (data, fn) => {
console.log(`Got data ${data} from client, sending response`);
// if client wants a response, send the response
if (fn) {
// no error here
fn({error: null, data: "Got your message"});
}
});
});
What you're seeing here is that socket.io is not really a request/response type protocol and socket.io has tried to shoehorn in a bit of a response around which you have to build your own structure.
Or, you can send an error object if there's an error:
// server
io.on('connection', socket => {
socket.on('someMsg', (data, fn) => {
console.log(`Got data ${data} from client, sending response`);
// if client wants a response, send the response
if (fn) {
// send an error here
fn({error: new Error("xxx Error")});
}
});
});
From server side there's no simple middleware function that validates the input data and returns error before the main logic is being executed.
I don't really understand what you're trying to use middleware for or to validate? the only place this data is present is on your message handler so any server-side validation you want to do on what the client sent needs to be there. You can certainly do that validation before you've send a response.
Shall I try to filter events by their names on the socket level to attach the needed functionality and send error in a way like next(new Error(...));? In this case there can be a work with error event listening, I guess.
Socket.io doesn't work like Express and I don't really see why you'd try to make it work that way. There is no next() involved in receiving a socket.io message so I'm not sure what you're trying to do there. There is an option for middleware when the socket.io connection is first made, but not for subsequent messages sent over that connection.
Is there a way to send response from server even if no callback is provided from client side?
If the client does not provide a callback, then the only way to send a response back to the client would be to send another message. But, the whole point of sending a response is if you have a cooperating client that is listening and expecting a response so the client may as well use the callback if they want the response. If the client doesn't want the response and won't code anything to receive it, there's nothing you can do about that.

JavaScript - How to create custom API listener for Socket.io events?

i'm trying to create custom API listener for 3-rd developers on my site. It should look like this:
// Function will be fired when any user joins to room
API.on('user-join', function(data){
console.log(data); // Data is object with user id and name
});
I have Socket.io listener too:
// This handler is fired every time when anyone joins
socket.on('user-join', function(data){
// Probably here i should call a callback to API.on('user-join') with that data object
});
How can i send callback in Socket.io handler to my custom created API? Please let me know how to do this. Thanks.
Not sure what you are trying to do, but if I understand correctly, you want to map the "on" function of the socket.io API to your API.
The best way to do this would be to implement a function like this:
API.on = (eventName, callback) => {
socket.on(eventName, callback);
}
Where eventName is a string and callback a function.
This way, you can add an event listener to your API, the same way you would with Socket.io
Eg:
API.on('testEvent', (obj) => {console.log(obj});
you are mixing things up.
your socket API will need a server-side listener independent from your socket API, that, as far as i understood, manages the IO with the connected clients, am i right?
In this case, you don't have to bind anything. You need a controller for your API sockets and your client's controller have to trigger events to those users.
a client connects to your site
a notification is received by the server
the server notifies to the API users
here's some pseudocode, please don't use it, it just illustrates a concept :D
const thrdUsers = [.....] // socket connections
const onThirdUserConnects = socket => thrdUsers.push(socket);
API.on('connect', onThirdUsersConnects);
// Then you need a broadcast function to send events to all your clients
const broadcastToAPI = (msg, val) => thrdUsers.forEach(s => s.send(msg, val));
// Your users will trigger events to your socket.io service
socket.on('whatever-event', (data) => {
//...Do stuff
// notify the API clients
broadcastToAPI('whatever-event', data);
});

Categories

Resources