javascriptmeteoriron-router

How to wait in one request till another request finishes the execution of same function in nodeJS


I am using meteor. I have a route defined in iron router as follows,

Router.route('/api/generatereceipt', {where: 'server'}).post(function(req, res, next) {
  console.log("API: generatereceipt invoked.");
  const reqBody = req.body;
  ....
}

I want to generate a receipt one at a time. i.e. the receipt needs to have a unique number which is incremental in nature.

Right now I'm reading the previously stored receipt number and incrementing it by one. I consider that as the receipt number for currently processing receipt.

However, This works fine as long as the generatereceipt API is not called concurrently by multiple clients. When the API is called simultaneously, the same receipt number is considered for all the concurrently processed receipts.

Therefore I want to make a request check if the same REST API is called by some other client in some other request is under process. If it is in the process then I want to wait till its execution in that request thread finishes.


Solution

  • There should be no issue with concurrent requests. See the following example:

    import { Meteor } from 'meteor/meteor'
    import { WebApp } from 'meteor/webapp'
    import { HTTP } from 'meteor/http'
    
    // create a simple HTTP route
    
    WebApp.connectHandlers.use('/api/generatereceipt', function (req, res, next) {
       // random 0 - 50ms timeout to simulate response delay
      const randomTimeout = Math.floor(Math.random() * 50)
    
      // use Meteor's Promise.await to await async in non-async functions
      // --> should prevent troubles with legacy iron-router
      const receipt = Promise.await(getReceipt())
      
      setTimeout(() => {
        res.writeHead(200)
        res.end(String(receipt))
      }, randomTimeout)
    })
    
    // increment id and return new value
    let receiptId = 0
    async function getReceipt () {
      return receiptId++
    }
    
    
    
    Meteor.startup(function () {
      let testCount = 0
      const url = Meteor.absoluteUrl('/api/generatereceipt')
    
      // simulate concurrent calls by using a timeout of 0
      function call (id) {
        setTimeout(Meteor.bindEnvironment(() => {
          // simulate calling the /api/generatereceipt route
          const response = HTTP.get(url)
          console.log(id, ' => ', response.content) // should match
        }, 0))
      }
    
      for (let i = 0; i < 25; i++) {
        call(testCount++)
      }
    })
    

    as you can see, the calls will resolve to the incremented ids:

    => Meteor server restarted                    
    I20200703-09:59:15.911(2)? 9  =>  9
    I20200703-09:59:15.912(2)? 7  =>  7
    I20200703-09:59:15.913(2)? 4  =>  4
    I20200703-09:59:15.913(2)? 0  =>  0
    I20200703-09:59:15.913(2)? 21  =>  21
    I20200703-09:59:15.913(2)? 24  =>  24
    I20200703-09:59:15.913(2)? 17  =>  17
    I20200703-09:59:15.913(2)? 18  =>  18
    I20200703-09:59:15.915(2)? 2  =>  2
    I20200703-09:59:15.917(2)? 19  =>  19
    I20200703-09:59:15.923(2)? 6  =>  6
    I20200703-09:59:15.923(2)? 23  =>  23
    I20200703-09:59:15.925(2)? 11  =>  11
    I20200703-09:59:15.928(2)? 8  =>  8
    I20200703-09:59:15.931(2)? 16  =>  16
    I20200703-09:59:15.932(2)? 10  =>  10
    I20200703-09:59:15.934(2)? 5  =>  5
    I20200703-09:59:15.934(2)? 13  =>  13
    I20200703-09:59:15.934(2)? 22  =>  22
    I20200703-09:59:15.936(2)? 20  =>  20
    I20200703-09:59:15.936(2)? 15  =>  15
    I20200703-09:59:15.939(2)? 14  =>  14
    I20200703-09:59:15.940(2)? 1  =>  1
    I20200703-09:59:15.940(2)? 3  =>  3
    I20200703-09:59:15.943(2)? 12  =>  12