How to implement the eventHandler function of socket.on() in nestjs - socket.io

Currently, I am making a chat application using nestjs and socket.io.
Below is the code created using socket.io in express.
// frontend (react)
// When the user enters the name of the room(submit), the function below is executed.
function handleRoomSubmit(event) {
event.preventDefault();
socket.emit("enter_room", roomName, ()=>console.log('Enter room!'));
}
// Backend(express)
socket.on("enter_room", (roomName, done) => {
socket.join(roomName);
done();
socket.to(roomName).emit("welcome", socket.nickname);
});
How can I implement the event handler function in the code above (the done() function above) in nestjs?

Related

Angular 9 and rxjs - wait for message event after postMessage

I am new to rxjs and not sure how to implement the follow logic. Any suggestion will be appreciated.
Background
I am going to implement the communication between host website and an iframe in it with postMessage. Since postMessage is one-way only, I would like to implement the logic to wait for 'response' by myself when a message is sent from host website to iframe.
I have a sync function called send(message) to invoke the postMessage to send message to iframe. Then I would like to have another function with the follow logic.
public async sendAndWait(message): Promise<responseObj> {
// 1. create an observable to wait to message event with timeout
// my first thought is as follow but I feel like it does not work
// fromEvent(window, 'message')
// .pipe(timeout(timeoutInMs))
// .subscribe(event => {
// console.info(event);
// });
// 2. run `send(message)` function
// 3. do not finish this function until timeout or receive event in the previous subscription.
}
When I use the function, I would like to have
let response = await sendAndWait(message);
Not sure if it is possible to implement? Thank you
You cannot stop code execution in JS (using Async-Await, a Promise object is returned behind the scenes. so that the code is never waiting)
Consider implementing it in the following way:
let response: responseObj;
function main(): void {
sendAndWait(MESSAGE_OBJECT).subscribe(x => response = x)
}
function sendAndWait(message): Observable<responseObj> {
send(message)
return fromEvent(window, 'message')
.pipe(
timeout(timeoutInMs),
first()
)
}
Or optionally returning Promise:
async function sendAndWait(message): Promise<void> {
send(message)
const response = await fromEvent(window, 'message')
.pipe(
timeout(timeoutInMs),
first(),
toPromise()
)
}

How to handle long async operations in Next.js to avoid slow page loading?

When using next-redux-wrapper how do I start a long asynchronous task so that it only runs on the client? I don’t want to use await on the server side since it would delay the initial page load. I’d rather set a loading flag as the task starts and show a loading indicator until it completes.
Let’s say my async operation looks like this:
function async takesLong(store) {
store.dispatch({type: “LOADING”, payload: true});
const result = await longOperation();
store.dispatch({type: “SETDATA”}, payload: data);
return store.dispatch({type: “LOADING”, payload: false});
}
I can call this in my Next page’s getInitialProps function like this:
MyPage.getInitialProps = async ({ store, isServer }) => {
const loader = takesLong(store)
if (isServer) await loader; // <-- will delay client response
return {
someprop: "some value"
};
};
This works well if the page loads on the client side. The operation starts, and my page can display a loading-spinner until the operation completes. But when started on the server side I have a long delay before the page displays at all. I’ve tried a number of approaches but can’t find one that works properly:
Starting the process on the server and not using await renders the page without the results being written to the store, so it has only set “loading” to true in the store and it never gets the data.
Passing store in my props doesn’t work - it ends up being an empty object ({ }) in the client.
Trying to run it inside my component doesn’t seem to work for a few reasons:
a) I don’t have the store object accessible in the React Component (only in getInitialProps which won’t get called on the client).
b) Even if I use actions instead of store.dispatch in the Component, when can I call it safely? I can’t do it during render since it will change the Redux state, and componentWillReceiveProps won’t get called before the first client-side render
Is there a well defined pattern for deferring a long operation to the client-side when using Next?
Do your long async task on componentDidMount, it will run only on client side.
React in SSR not runs componentDidMount lifecycle hook.
Using bound actions during componentDidMount works. Thanks to #eenagy for the suggestion. Doing things in this order seems to do what is needed:
import { bindActionCreators } from "redux";
import { setLoading, setError, setData } from "../actions";
componentDidMount() {
if (!this.props.haveData && !this.props.loading && !this.props.error) {
this.props.setLoading(true);
loadSomeData() // <-- this takes a while to complete
.then( data => {
this.props.setData(data);
this.props.setLoading(false);
})
.catch( err => {
this.props.setError(err);
this.props.setLoading(false);
});
}
}
render() {
if (this.props.loading) return (<Loading/>);
return (/*regular page*/);
}
export const mapDispatchToProps = dispatch => {
return bindActionCreators({ setLoading, setError, setData }, dispatch);
};
export default connect(mapStateToProps, mapDispatchToProps)(Component);
This way if the initial data is not already loaded (say by another page) it will get kicked off when the
component mounts and run asynchronously until the operation completes and
calls the actions in redux to cause the page to re-render.

switch final emitted action from effect based on payload

I have an app that uses ngrx
Once a client updates a product, it uses a websocket to update all clients.
This works, by subscribing to the socket, so after a next method is called on the socket, it calls an action that handles the side effects of updating
But, now when it comes to deleting and adding, I'd like to use the same socket effect but change its final action call
Or if someone can suggest a better way
Socket service:
export class SocketService {
socket$ = Observable.webSocket( 'ws://localhost:1234');
}
effects:
//This is called from component to start the update process
#Effect({dispatch:false}) beginUpdate$: Observable<any> = this.actions$
.ofType<fromBlogActions.BlogUpdateStartAction>(fromBlogActions.BLOG_UPDATE_START_ACTION)
.map((action:any)=>{
console.log(action)
return action.payload;
})
.do((action)=> this.socketService.socket$.next(JSON.stringify(action)))
//Calls the next method to send data to the websocket
//The below watches for data emitted from the websocket
//Then calls the BlogUpdatedAction, what I need is for it to call a different action based on action type
#Effect() watchSocket$ = this.socketService.socket$
.map((action:BlogPayLoad)=>{
console.log(action)
return action
})
.mergeMap((action)=> [new fromBlogActions.BlogUpdatedAction(action)])
It should be possible like this:
#Effect() watchSocket$ = this.socketService.socket$
.map((action:BlogPayLoad)=>{
console.log(action)
return action
})
.mergeMap((action)=> {
if(action.type === 'BlogAddAction'){
return new fromBlogActions.BlogAddAction(action))
else if (...) {
....
}
else if (action.type === 'BlogUpdatedAction'){
return new fromBlogActions.BlogUpdatedAction(action))
})

Is it possible to add several handlers to the same socket.io.on event?

In my game application I would like to have general class for handling socket connection and authorization and several classes for event handling, in such a way:
//loading game/lobby/other socket routers
...
var socketRouter = function(server) {
var io = Io(server);
io.use(socketioJwt.authorize({
secret: config.secretKey,
handshake: true
}));
io.on("connection", function(socket) {
lobbySocketRouter(socket);
gameSocketRouter(socket);
//other routers
...
socket.on("disconnect", function() {
console.log("DISCONNECTED", socket.id);
});
});
};
It's not a problem to generate unique event names for different routers to not interfere with each other. The problem is in disconnect event - I want every router having possibility to perform right action on it. Is it correct to add own handler to disconnect event in every router like this so each of them would trigger:
//lobbySocketRouter
socket.on("disconnect", function() {
//handling lobbySocketRouter special actions
});
...
//gameSocketRouter
socket.on("disconnect", function() {
//handling gameSocketRouter special actions
});
?
I want every router having possibility to perform right action on it. Is it correct to add own handler to disconnect event in every router like this so each of them would trigger:
"route" I guess you're talking about the Namespaces, you can handle multiples "routers", and then treat each disconnection event depending on the Namespace.
I've wrote in my previous answer a template application to get multiple namespaces inside an Array:
socket.on('disconnect', disconnectCallback(socket,ns));
function disconnectCallback(socket,ns) {
return function(msg) {//we return a callback function
if(ns==="blabla") {
console.log("Disconnected from blabla");
socket.broadcast.send("It works! on blabla");
}
.
.
.
}
};
You can then create multiple disconnection behaviors based on the Namespace, hope it helps.
Please let me know if you need something else :-)

Jasmine test event with asynchronous call

The issue is to test the event handlers with asynchronous internal methods, which is executed by SDK like facebook.
the plain test is:
describe('Listens to someevent', function () {
it('and triggers anotherevent', function () {
var eventSpy = spyOnEvent(document, 'anotherevent');
var data = {
param1: 'param1',
param2: 'param2',
}
this.component.trigger('somevent', data);
runs(function() {
expect(eventSpy).toHaveBeenTriggeredOn(document);
});
});
});
when someevent is triggered with options, the component handler is fired:
this.handler = function (e, data) {
SDK.apicall(data, function (err, response) {
if (!err) {
doSomething();
}
// trigger data event
that.trigger(document, 'anotherevent');
});
}
;
};
In jasmine 1.3 and before, a runs without a preceding waitsFor still executes immediately. It's really the waitsFor that makes the spec asynchronous. This has changed in 2.0.
Alternatively, if you don't really want to call the external API during your tests. You can use something like jasmine-ajax (docs). This will allow you to return the ajax call immediately in test with whatever response you want to test with. This has the advantage of making your specs faster (there's no waiting for the API), and make your specs less dependent on the API being up when they run.

Resources