Search code examples
javascriptreactjssetintervalschedule

How to develop a scheduler date application using JavaScript


I have a noob doubt about how to code this scenario using JavaScript.

Develop a data structure to help scheduling airplanes to take off and land from this runway.

Each time a plane takes off or lands, it needs exclusive use of the runway starting 10 minutes before the scheduled runway use, and ending 10 minutes after the scheduled runway use (assume the actual take-off or landing is instantaneous).

Be sure that planes don’t have overlapping schedules.

  1. The Scheduler class will be call using ReactJS.

  2. The class needs to be tested as well using babel and mocha. (I care about that later)

I evaluated the use of schedule.js but this is not the case. I think to use setInterval() to schedule the time, but I don't know if it a good strategy.

The task is to implement a scheduler with approximately the following API:

class Scheduler {
    // returns true if there'ss room to schedule at `time`
    CouldScheduleAt(Date time)

    // returns true if we successfully scheduled
    ScheduleAt(Date time)

    // Choose an available time to schedule at, and return that time
    Schedule()

    // returns true if we successfully unscheduled something
    UnscheduleAt(Date time)
}

So.. How I can developer this scenario using JavaScript?

Edit - I implemented based on Tiago Henrique implementation.

class Scheduler {
    constructor(timer) {
        this.dates = new Set()
        this.planes = {}
        this.timer = timer
    }

    isPlaneInList(time) {
        return (time in this.planes)
    }

    isDatesInconflicting(scheduledDate, time) {
        if (time.getTime() <= scheduledDate.getTime()) return true

        return (time.getTime() > this.timer)
    }

    // returns true if there's room to schedule at `time`
    couldScheduleAt(time) {
        if (this.dates.has(time)) return false

        Object.keys(this.dates).map(scheduledDate => {
            if (this.isDatesInconflicting(scheduledDate, time)) {
                return false
            }

            return time
        })

        return true
    }

    // returns true if we successfully scheduled
    scheduleAt(time, plane) {
        if (!this.couldScheduleAt(time)) return false

        this.dates.add(time)
        this.planes[time.toString()] = plane

        return this.isPlaneInList(time.toString())
    }

    // Choose an available time to schedule at, and return that time
    schedule() {
        // ......
    }

    // returns true if we successfully unscheduled something
    unScheduleAt(time) {
        const plane = this.planes[time.toString()]
        if (!plane) return false

        delete this.planes[time.toString()]
        this.dates.delete(time)

        return !this.isPlaneInList(time.toString())
    }
}

export default Scheduler

I don't be sure about isDatesInconflicting implementation but there is for now. The schedule function there's nothing in this moment.

References:


Solution

  • If there's no priority involved you simple need a queue where the dequeue method should wait for 20 minutes to dequeue the next item. This can be achieved using a Promise and setTimeout. e.g.

    class PlaneQueue {
      constructor() {
        this.dequeueChain = Promise.resolve()
        this.planes = []
      }
    
    
      enqueue(plane) {
        this.planes.push(plane)
      }
    
      dequeue() {
        // this is returned to the caller so it gets the next plane
        const intermediateChain = this.dequeueChain.then(() => {
          return this.planes.shift()
        })
        // update the internal promise to resolve again only after 20 minutes
        // this will cause the next dequeue call to wait for 20 minutes
        this.dequeueChain = intermediateChain.then(() => {
          return new Promise((resolve) => setTimeout(resolve, 1200000))
        })
        return intermediateChain
      }
    }
    
    
    const planesQueue = new PlaneQueue()
    planesQueue.enqueue({ name: 'Airfrance' })
    planesQueue.enqueue({ name: 'American Airlines' })
    planesQueue.enqueue({ name: 'Latan' })
    
    planesQueue.dequeue().then((plane) => console.log(`${plane.name}: ${new Date()}`))
    planesQueue.dequeue().then((plane) => console.log(`${plane.name}: ${new Date()}`))
    planesQueue.dequeue().then((plane) => console.log(`${plane.name}: ${new Date()}`))
    

    This works by taking advantage of the ability promises have to be chain. When we use then and return a new Promise inside it, the result is a new promise that fulfills only when all "chained" promises are fulfilled too.

    The idea here is that we chain a new promise, that resolves after 20 min, every time dequeue is called, causing the next call to effectively have to wait 20min for the next plane, and so on.

    EDIT

    I didn't really see the API you were trying to follow at first, the code above will not fit in that case. What you need is a list of scheduled dates and a map to link dates with planes. To check if you can schedule something you will have to go trough all dates and check that there isn't a date less than 20 mins before or after.

    class PlaneQueue {
      constructor() {
        this.dates = new Set()
        this.planes = {}
      }
    
      couldScheduleAt(date) {
        if (set.has(date)) return false
        for (scheduledDate of dates) {
          // check the date is not less than 20 minutes before or after
          if (conflicting(scheduledDate, date)) {
            return false
          }
        }
        return true
      }
    
    
      scheduleAt(date, plane) {
        if (!couldScheduleAt(date)) return false
    
        this.dates.add(date)
        this.planes[date.toString()] = plane
        return true
      }
    
      unscheduleAt(date) {
        const plane = this.planes[date.toString()]
        if (!plane) return false
    
        delete this.planes[date.toString()]
        this.dates.delete(date)
        return plane
      }
    }
    

    An even better approach would be to use a list structure that keeps its items in order, so you would be able to exit the loop early (you only need to go to the position where the new date would be placed and check if it is not conflicting).

    How you'd use this inside a react component is not really important, as long as this schedule mechanism is correct you could use it anywhere, not just inside a react component.