Search code examples
javascriptnode.jscallbackpromisenode-rules

Converting callback to Promise when I dont have the return value


I am working on server that uses hapi and executes rules from node-rules.

I have a callback which is called by the R.execute method from node-rules. I need to return a Promise from the exec method as the result of executing the callback.

Code

const callback = data => {
  const {matchPath, result} = data
  descision.setMatchPath(matchPath)
  if (!result) {
    descision.addMessage(
      'No match could be found in the rules provided, either incorrect or non-matching information was provided'
    )
  }
}

function exec (input) {
  const {medicineType, facts: data} = input
  const R = new RuleEngine()
  R.register(rules)
  if (medicineType !== 'generic') {
    const facts = {
      data
    }
    R.execute(facts, callback)
  }
}

I noticed from the source code that R.execute does not return anything that I can use. I notice that in execute calls this function here recursively but does not terminate without the callback.

How can I convert this to a function that returns a Promise?


Solution

  • While browsing through some of the answers for other questions, I remembered the $.deferredand Q.defer API, I found a solution that resembles them:

    1. creates a deferred

    2. passes the deferred to the callback

    3. uses the deferred and resolve the promise

    4. and most importantly, return the promise that was created by the deferred

    I did not want a custom Promise implementation or to monkey-patch the Promise API. If the latter is not a problem, there are several modules on npm which do this and polyfill Promise.defer.

    The defer function is from here

    The code now looks like:

    /* eslint-disable promise/param-names */
    function defer () {
      let resolve, reject
      const promise = new Promise(function (...args) {
        resolve = args[0]
        reject = args[1]
      })
      return {resolve, reject, promise}
    }
    
    /* eslint-enable promise/param-names */
    
    const makeCallback = deferred => data => {
      const {matchPath, result} = data
      descision.setMatchPath(matchPath)
      if (!result) {
        descision.addMessage(
          'No match could be found in the rules provided, either incorrect or non-matching information was provided'
        )
      }
      deferred.resolve(descision)
    }
    
    function exec (input) {
      const {medicineType, facts: data} = input
      const R = new RuleEngine()
      R.register(rules)
      if (lenderType !== 'generic') {
        const facts = {
          data
        }
        const deferred = defer()
        const callback = makeCallback(deferred)
        R.execute(facts, callback)
        return deferred.promise
      }
    }