Fairplay89
Fairplay89

Reputation: 147

Function Return not Working

I am working in Node and trying to load the next sequence from my db. I am able to access the db, load and return the sequence within my function, but I am not able to access it outside of the function.

function getRunId() {
        counters.findOne({_id: 'Run_ID'}, function(err, resp) {
            if(err) {
                console.log(err);
            }
            console.log('Seq: ' + resp.sequence); // Console Output = Seq: 1234
            return resp.sequence;
        });
    };

    var currentRunId = getRunId(); 
    console.log('Run_ID: ' + currentRunId); // Console Output = CRID: undefined

I've checked several pages worth of Stack Overflow issues relating to using callback's, async (node module), how to properly return values in the function, etc... but none of them get me closer to accessing currentRunId outside of the function.

Is this issue further complicated by the use of Mongo queries inside my function?

Upvotes: 1

Views: 3679

Answers (2)

Joe Creager
Joe Creager

Reputation: 56

For anyone stumbling on this later, start by reading this answer.

I've dealt with this a few times so I understand the frustration. You are trying to mix sync and async code by doing this:

var currentRunId = getRunId(); 
console.log('Run_ID: ' + currentRunId);

The trouble is that console.log('Run_ID: ' + currentRunId) is called immediately after you invoke getRunID() by assigning it to current RunID, and getRunID() resolves after console.log('Run_ID: ' + currentRunId), causing the currentRunId variable to be undefined.

But, you have some options to deal with this. Option one is to return a callback, and log the results of the callback instead. Option 2 is to use an ES6 promise. To use option 2, you need node version 7, and you need to use 'use strict' in your code.

Here are 3 examples built around a function stub that spoofs the results of findOne(). The getRunIdA() is your function, and getRunIdB, and getRunIdC are two example solutions to your current problem.

'use strict'

// A function stub which represents a simplified version of findOne.
// Accepts callback and returns a callback with the results of data
function findOne (callback) {
  var data = {
    sequence: 6
  }
  return callback(null, data)
}

// This is a simplified version of your function, which reproduces the undefined result
function getRunIdA () {
  findOne(function (err, resp) {
    if (err) {
      console.log(err)
    }
    console.log('Seq: ' + resp.sequence)
    return resp.sequence
  })
}

// This is your function with a callback
function getRunIdB (callback) {
  findOne(function (err, resp) {
    if (err) {
      console.log(err)
    }
    console.log('Seq: ' + resp.sequence)
    return callback(resp.sequence)
  })
}

// This is your function with a promise
var getRunIdC = new Promise(function (resolve, reject) {
  resolve(findOne(function (err, resp) {
    if (err) {
      console.log(err)
    }
    return resp.sequence
  }))
})

// Invoke your funciton; get undefined
var currentRunID = getRunIdA()
console.log('Run_ID: ' + currentRunID) // Run_ID: undefined

// Invoke getRunIdB using callback, get 6
getRunIdB(function (id) {
  console.log('Run_ID: ' + id) // Run_ID: 6
})

// Invoke getRunIdC with a promise; get 6
getRunIdC.then(function (currentRunID) {
  console.log('Run_ID: ' + currentRunID) // Run_ID: 6
})

/*
results for all 3:

Seq: 6
Run_ID: undefined
Seq: 6
Run_ID: 6
Run_ID: 6
*/

Give this a try by saving to your machine and running:

node test.js

Is this issue further complicated by the use of Mongo queries inside my function?

Nope, you just need to pass the results of your query to a promise or a callback so that you can work with the results somewhere else.

I hope this helps!

Edit: OP added the following code in a comment, which I will try to break down and address.

Unfortunately, using getRunIdB results in callback is not defined and using getRunIdC results in currentRunId is not defined

var currentRunID = ''; 
var getRunId = new Promise(function (resolve, reject) { resolve(counters.findOne({_id: 'Run_ID'}, function (err, resp) { 
  if (err) { 
    console.log(err) 
  } 
  return resp.sequence; 
  })) 
}); 
getRunId.then(function (res) { 
  console.log('Run_ID: ' + res.sequence) // Run_ID: 1234 
  currentRunID = res.sequence; 
}) 
console.log(currentRunID); // currentRunID is not defined

Check out an answer I gave to a similar question for more details on the JS concurrency model. Simply put, the getRunID() function is executing asynchronous code. What that means is that getRunID() doesn't get inserted into the message queue that determines what order javascript will execute until it's callbacks are completed. Thus, when you log currentRunID outside of the .then() function, the results is undefined because currentRunID is undefined.

I think that ultimately what OP is trying to do is to export the result of the function so that the something can be done with those results, this needs to be done within a callback like so:

getRunId.then(function (res) { 
  // Do stuff with the run ID here.
}) 

Upvotes: 2

Dalorzo
Dalorzo

Reputation: 20014

You are only returning on a callback function but not on the actual function.. Change your code to this:

function getRunId() {
        var result = counters.findOne({_id: 'Run_ID'}, function(err, resp) {
            if(err) {
                console.log(err);
            }
            console.log('Seq: ' + resp.sequence); // Console Output = Seq: 1234

        return resp.sequence;
    });
    return result; //<-- return result of your function is here
};

var currentRunId = getRunId(); 
console.log('Run_ID: ' + currentRunId); 

Upvotes: 1

Related Questions