Umbro
Umbro

Reputation: 2204

Multiple axios get requests in socket.io. Sending two events via `socket.emit` at the same time

I am trying to create multiple axios requests in socket.io. If I have simultaneously:

socket.emit ("Emit1", req1.data); socket.emit ("Emit2", req2.data);

socket.emit("Emit2", req2.data); works. socket.emit ("Emit1", req1.data); does not work.

When I remove socket.emit("Emit2", req2.data); --> emit1 works.

How to combine it so that it works simultaneously?.

Can I create two servers. One supporting the first component and request get url1 and a second server that supports requesturl2?

const Emit = async socket => {
    try{
      const [req1, req2] = await Promise.all([
        axios.get(url1, {
          headers: {

          }
        }),
        axios.get(url2, {
          headers: {
            'Authorization': `Bearer ${token}`
          }
        })
      ]);

      socket.emit("Emit1", req1.data);
      socket.emit("Emit2", req2.data); 
    }

    catch(error){
        console.log(error)
    }
};

Client

First component:

import socketIOClient from "socket.io-client";

componentDidMount() {
    axios.get
        axios({
            url,
            method: "GET",
            headers: {
                'Authorization': `Bearer ${token}`
            }
        })
        .then(res => {
            this.setState({
                scores: res.data,
                activeTab: res.data[0].id
            });
        })
        .catch(error => {
            console.log(error);
        })      
}


componentDidUpdate(prevProps, prevState) {
    const endpoint = 'http://127.0.0.1:3000'; 

    if (prevState.scores !== this.state.scores) {
        const socket = socketIOClient(endpoint);
        socket.on("Emit1", data => this.setState({ 
            scores: data 
        }));
    }
}

Second component:

import socketIOClient from "socket.io-client";

componentDidMount() {
    axios.get
        axios({
            url,
            method: "GET",
            headers: {
                'Authorization': `Bearer ${token}`
            }
        })
        .then(res => {
            this.setState({
                abcd: res.data
            });
        })
        .catch(error => {
            console.log(error);
        })      
}


componentDidUpdate(prevProps, prevState) {
    const endpoint = 'http://127.0.0.1:3000'; 

    if (prevState.abcd !== this.state.abcd) {
        const socket = socketIOClient(endpoint);
        socket.on("Emit2", data => this.setState({ 
            abcd: data 
        }));
    }
}

Upvotes: 0

Views: 4719

Answers (1)

Dupocas
Dupocas

Reputation: 21317

I've been working on your question for a while now and this are my conclusions:

Event emission works just fine

I've reconstructed your backend this way:

const express = require('express')
const app = express()
const http = require('http').createServer(app)
const io = require('socket.io')(http)
const port = 3003

app.use(express.json())

app.get('/emit', async({ res }) => {
    const [data1, data2] = await Promise.all([fakeApiCall(1), fakeApiCall(2)])
    io.emit('Emit1',data1)
    io.emit('Emit2',data2)
    res.status(200).json('emitted')
})

const fakeApiCall = id =>{
    return new Promise((resolve, reject) =>{
        setTimeout(() => resolve(`data${id}`), 3000)
    })
}

http.listen(port, () => console.log('listening on port ' + port))

Essencially that's exactly what you are doing, and works just fine, both events are emmited after Promise.all solves all promises.

Frontend setup works

After that I've made a version of your frontend:

App.js:

import React from 'react'
import ComponentA from './ComponentA'
import ComponentB from './ComponentB'

const App = () => {
    return (
        <>
            <ComponentA />
            <ComponentB />
        </>
    )
}


export default App

Component A :

import React from 'react'
import socketIOClient from 'socket.io-client'

class ComponentA extends React.Component {
    state = {
        data: null
    }

    componentDidMount(){
        setTimeout(() => this.setState({data: 'bla'}), 2000)
    }


    componentDidUpdate(prevProps, prevState) {
        const endpoint = 'http://127.0.0.1:3003';

        if (prevState.data !== this.state.data) {
            const socket = socketIOClient(endpoint);
            socket.on("Emit1", data => this.setState({ data }))
        }
    }

    render() {
        const { data } = this.state
        return (
            <div>
                {data}
            </div>
        )
    }
}
export default ComponentA

Component B :

import React from 'react'
import socketIOClient from 'socket.io-client'

class ComponentB extends React.Component {
    state = {
        data: null
    }

    componentDidMount(){
        setTimeout(() => this.setState({data: 'bla'}), 2000)
    }


    componentDidUpdate(prevProps, prevState) {
        const endpoint = 'http://127.0.0.1:3003';

        if (prevState.data !== this.state.data) {
            const socket = socketIOClient(endpoint);
            socket.on("Emit2", data => this.setState({ data }))
        }
    }

    render() {
        const { data } = this.state
        return (
            <div>
                {data}
            </div>
        )
    }
}
export default ComponentB

And surprisingly... It works just fine! So, what is the difference here? Everything looks the same (how the events are emitted, how the listener is only inserted in componentDidUpdate, how the state changes after an async call in componentDidMount). So the only thing that can possibly be interfering is the axios call you make inside componentDidMount, cause in both components the io.listen isn't setted at componentDidMount how it should be (I assume you have reasons for that), so both components MUST update the state before the event is emmited, otherwise if the properties data or abcd don't get updated in time the event listener will be called after the backend emits the data. And your claim about how if you omit Emit2 Emit1 will work just fine. But, what about omit Emit1? Emit2 will work as well? Looks like a race condition, something in componentA's componentDidMount is preventing componentB from updating or vice-versa

Possible Solution

Place the io's listeners in componentDidMount in both components to determine if this is the case. Cause honestly, I couldn't think in any other problem that may be causing this.

Upvotes: 1

Related Questions