0

My connections manager is expected to receive connect requests nondeterministically and to handle them in sequence.

How can asynchronous operations be queued such that they are handled later?

I'm not sure of what objects to place in a queue.

Here is my beginner's attempt. Thanks for any help!

class RequestQueue {
  private _requests = [];
  constructor() {}

  public add( rq: any ) {
    this._requests.unshift(rq);
  }

  public remove() {
    return this._requests.pop();
  }
}

class ConnectionManager {
  private requestQueue;
  private connecting: boolean = false;

  constructor() {
    this.requestQueue = new RequestQueue();
  }

  // ConnectionManager is expected to receive connection requests
  // nondeterministically and to handle them in sequence.
  public connect(id: string) {
    if (!this.connecting) {
      this.connecting = true;
      return this.asyncConnect(id)
      .then(
        (result) => result,
        (err) => err
      )
      .then( () => {
        this.connecting = false;
        if (this.requestQueue.length > 0) {
          return this.requestQueue.remove();
        }
      });
    } else {
      // how do I queue a someAsyncOp for later?
    }
  }

  private asyncConnect(id: string) : Promise<any> {
    return new Promise( (resolve, reject) => {
            console.log('begin connect to ', id);
            setTimeout(() => {
                console.log('end connect to ', id);
                resolve();
            }, 3000);
        });
  }
}

function makeConnections() {
  const connectionManager = new ConnectionManager();
  connectionManager.connect('A');
  connectionManager.connect('B');
  connectionManager.connect('C');
  connectionManager.connect('D');
}

makeConnections();

https://codepen.io/cssbog/pen/BaBMzWW

FZs
  • 16,581
  • 13
  • 41
  • 50
user2132190
  • 373
  • 1
  • 2
  • 17
  • 1
    `public`, `private`?! Are you writing plain JS? This looks like TypeScript... – FZs Sep 23 '19 at 20:03
  • @FZs Yes--Typescript – user2132190 Sep 23 '19 at 20:05
  • If you're using promises, then `async`/`await` can be used to synchronize. Until you understand Promises you're going to be in the dark, though. They're subtly tricky, but once you get it then it's a lot easier to work with them. [Bluebird](http://bluebirdjs.com/docs/getting-started.html) has additional functions like `Promise.map` which can be handy for breaking out multiple operations, example: `Promise.map([ 'A', 'B', 'C', 'D' ], label => connectionManager.connect(label))` – tadman Sep 23 '19 at 20:09
  • One thing you're doing here that's working against you is defining error handlers at each stage. Consider using `then(...).catch(err => ...)` instead. It's not clear what `(result) => result` is supposed to do since you discard that value in the next `then`. – tadman Sep 23 '19 at 20:10
  • Have a look at [this thread](https://stackoverflow.com/questions/47157428/how-to-implement-a-pseudo-blocking-async-queue-in-js-ts/47157577#47157577). Your `connecting` property should be a promise that you can chain something onto. – Bergi Sep 23 '19 at 20:29

1 Answers1

0

You have to implement the async serializer pattern

The solution is to keep a queue of Promises that chains them one after the other. It is just a few lines of code and it is a general purpose, allowing any function to be serialized:

const serialize = (fn) => {
    let queue = Promise.resolve();
    return (...args) => {
        const res = queue.then(() => fn(...args));
        queue = res.catch(() => {});
        return res;
    };
};