Question

I'm having trouble returning a value from a closure function in Javascript. Originally I had a return statement in the inside function, but that didn't seem to work. I moved the return statement to the outer function, and it still returns undefined. Shouldn't returnVal be given a value in the inner function before being returned in the outer function?

console.log('Returned: ' + getColumn('CPC'));

function getColumn(header) {
  var returnVal = undefined;

  ptor.findElements(protractor.By.className('ngHeaderText')).then(function(headers) {

    for (var i = 0; i < headers.length; i++) {
      (function(i){
        headers[i].getText().then(function(headerResult) {
          if (headerResult == header) {
            console.log('i: ' + i);
            returnVal = i;
          }
        });
      })(i);
    }
  });

  return returnVal;
};

Any help would be greatly appreciated!

EDIT: moved the return statement

Was it helpful?

Solution

You can't do a "non-local return" in Javascript, where an inner function returns to an outer function.

That said, your problem here is not the closures, its that you need to code your control flow using the promises. Javascript doesn't "pause" the current function if it emits an async request (like C# or Python would if you do a yield statement) and instead just keeps trucking on. This means that you are calling code to start an async request but going on and returning undefined before the request is complete. What you want to do, is instead immediately return a promise (instead of returning undefined) and do all your control flow inside the promise event handler, using promise-specific methods. (It kind of sucks that you can't use return and for loops as usual but that is a Javascript wart you will have to live with until everyone starts using ES6 generators)

First of all, getColumn should return a promise containing the value of the column. Right now, it does some computation but doesn's return anything:

function getColumn(header){
     return ptro.findElements(...);
   // ^^^^^ 
}

Secondly, you usually can't just put async code inside a loop like that. First you need to choose if you want to do the requests in sequence (get the first header, when its done get the second header, etc) or if you want to fire all the header requests ar once and wait for the results. These will have to be coded differently and how you write them will depend if you have to dcode everything by hand and what helper lib you are using for the promises.

For example, if you want to do the processing sequentially, one way is to use recursion instead of a for loop.

then(function(headers) {

    function loop(i){
        if( i >= headers.length) {
            return null; //not found
        }else{
            return headers[i].getText().then(function(headerResult) {
                if (headerResult == header) {
                    console.log('i: ' + i);
                    return i;
                }else{
                    return loop(i+1);
                }
             });
         }
     }

     return loop(0);
});

The code for firing the requests in parallel will vary slightly depending on what promise library you are using. Hopefully you should have some sort of library function for doing that.

OTHER TIPS

Your logic is correct, you can return a value from an inner function to an outer function and then return that value from the outer function. This much simplified example derived from your code shows this:

console.log('Returned: ' + getColumn());

function getColumn() {

  var returnVal = undefined;
  (function(){
      returnVal = 'test';     
  })();  

  return returnVal;
};

This results in the following message in the console:

Returned: test

The problem is that you are returning from the function passed to when() but not from the function that this itself is inside

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top