Search code examples
javascriptsingletonsynchronized

How to synchronize a method in javascript?


I am trying to synchronise a singleton. I would need to make this method like the equivalent of synchronized in java. What happens to me is that because the socket takes a while, if the first two requests are very close together I get two websockets created. (Then from the third one onwards it takes the instance correctly).


import io from 'socket.io-client';

export default class SocketIo {
  static socket = null;

  static instance = null;

  async initialize() {
    this.socket = await io(`http://${ip}:10300/`, {
      transports: ['websocket'],
    });
  }

  static async getInstance() {
    logger.info('socketIo.api.getInstance: BEGIN');
    if (!this.instance) {
      logger.info('socketIo.api.getInstance: creating new socket instance...');
      try {
        const o = new SocketIo();
        await o.initialize();
        this.instance = o;
        logger.info('socketIo.api.getInstance: socket instance created SUCCESSFULLY');
      } catch (e) {
        moaLog('socketIo.api.getInstance: ERROR: ', e);
        throw e;
      }
    } else {
      logger.info('socketIo.api.getInstance: a socket instance already exists, reusing that one');
    }
    logger.info('socketIo.api.getInstance: END');
    return this.instance;
  }
}

in main.js

var socket1 = SocketIo.getInstance();
var socket2 = SocketIo.getInstance();

// ... after a while
var socket3 = SocketIo.getInstance();



2022-06-16T17:53:40.658Z: socketIo.api.getInstance: BEGIN
2022-06-16T17:53:40.660Z: socketIo.api.getInstance: creating new socket instance...
2022-06-16T17:53:41.140Z: socketIo.api.getInstance: BEGIN
2022-06-16T17:53:41.141Z: socketIo.api.getInstance: creating new socket instance...
2022-06-16T17:53:41.379Z: socketIo.api.getInstance: socket instance created SUCCESSFULLY
2022-06-16T17:53:41.382Z: socketIo.api.getInstance: END
2022-06-16T17:53:41.411Z: socketIo.api.getInstance: socket instance created SUCCESSFULLY
2022-06-16T17:53:41.415Z: socketIo.api.getInstance: END
...
2022-06-16T17:56:13.076Z: socketIo.api.getInstance: BEGIN
2022-06-16T17:56:13.078Z: socketIo.api.getInstance: a socket instance already exists, reusing that one
2022-06-16T17:56:13.079Z: socketIo.api.getInstance: END

And from server view I see two websocket connections.

Any ideas?


Solution

  • I solved using async-lock.

    import AsyncLock from 'async-lock';
    const lock = new AsyncLock();
    
    export default class SocketIo {
      // ...
    
      static async getInstance() {
        logger.info('socketIo.api.getInstance: BEGIN');
        if (!this.instance) {
          logger.info('socketIo.api.getInstance: creating new socket instance...');
          try {
            await lock.acquire('socketIo', async () => {
              if (!this.instance) {
                const o = new SocketIo();
                await o.initialize();
                this.instance = o;
                logger.info('socketIo.api.getInstance: socket instance created SUCCESSFULLY');
              }
            });
          } catch (e) {
            moaLog('socketIo.api.getInstance: ERROR: ', e);
            throw e;
          }
        } else {
          logger.info('socketIo.api.getInstance: a socket instance already exists, reusing that one');
        }
        logger.info('socketIo.api.getInstance: END');
        return this.instance;
      }
    }