0

I don't know if this is could be technically called a race condition...

What I have is a big baby, which can only perform one action at the time, but it's being called from an api endpoint; so simultaneous calls can occur

What I think I need to do is somehow make a queue of actions, return a promise to whoever created it, execute the actions synchronously, and resolve the promise with the value returned by the action

Here is the code (it's no real btw, just a snippet representing the problem):

class Baby {
  constructor() {
    this.current = 'A'
  }

  go(from, to) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (from === this.current) {
          this.current = to
          resolve()
        } else {
          reject(new Error('Unexpected state'))
        }
      }, 100)
    })
  }

  // In order to perform the action successfully it has to do some steps in some exact order
  async doAction() {
    await this.go('A', 'B')
    await this.go('B', 'C')
    await this.go('C', 'A')

    console.log('OK')
  }
}

module.exports = new Baby()

And is called like this:

const baby = require('./baby')

for (let i = 0; i < 5; i++) {
  doAction()
}

Thanks in advance!

Fabian Mendez
  • 512
  • 5
  • 15
  • i dont see any promise here – Basil Battikhi Feb 04 '18 at 13:26
  • 1
    You should [never export a singleton `class` instance](https://stackoverflow.com/a/48368123/1048572). – Bergi Feb 04 '18 at 13:44
  • Yes, it's called a race condition. You don't need multithreading for it, just concurrency. – Bergi Feb 04 '18 at 13:45
  • 1
    [Queueing](https://stackoverflow.com/a/27319607/1048572) [promises](https://stackoverflow.com/a/18952698/1048572) could not be simpler - go for it! – Bergi Feb 04 '18 at 13:50
  • Ok, I'm now reading about queueing promises, and about the singleton class instance, I've never heard about it. I just learned it thanks to you :) – Fabian Mendez Feb 04 '18 at 15:07

1 Answers1

0

Thanks to Bergi's tips, this is the final solution:

Basically, this keeps a chain of promises and when a new action needs to be added it's chained to the current chain of promises

const baby = {
  current: 'A',
  action: Promise.resolve(),

  go(from, to) { 
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (from === this.current) {
          this.current = to
          resolve()
        } else {
          reject(new Error('Unexpected state'))
        }
      }, 100)
    })
  },

  doAction() {
    this.action = this.action.then(async () => {
      await this.go('A', 'B')
      await this.go('B', 'C')
      await this.go('C', 'A')

      console.log('OK')
    })
  }
}


baby.doAction()
baby.doAction()
baby.doAction()
Fabian Mendez
  • 512
  • 5
  • 15