1

I have fairly simple task but it makes me pull my hair out. Already searched whole Internet and none of the solutions found can be directly translated to my problem. It's a follow up question to JavaScript - no return

Here's what I got:

var worksheetArray;
var filtersArray =[];

function testu(){

  filtersArrayFetch();
  console.log("finished fetching");
  console.log(filtersArray);
  //do more stuff with array

}

function filtersArrayFetch()
{

    workbook = viz.getWorkbook();
    sheet=viz.getWorkbook().getActiveSheet();
    worksheetArray = sheet.getWorksheets();


        for (var i=0; i<worksheetArray.length; i++) {
          (function(num){
            worksheetArray[i].getFiltersAsync()
                .then(function(promise){
                    for (j=0;j<promise.length;j++)
                    {
                        filtersArray.push(promise[j].getFieldName());
                    }
               })

          })(i);  

         }
console.log("after for");

}

In plain English -I have an array of worksheets fetched with synchronous Tableau API function. getFiltersAsync() returns Promise as an Array. On this Array I would like to perform getFieldName and push it to final Array I will be using later in the code. Code works until worksheetArray[i].getFiltersAsync() but does not evaluate .then() and returns undefined to testu() function. testu() is being called by button click. I need to keep IE(Edge) compatibility so Promise.all() is not an option.

What is wrong with the script? Why it's not evaluating .then()?

EDIT:

I've managed to solve the problem using self-invoking runner function:

function filtersearch2(i){
    workbook = viz.getWorkbook();
    sheet=viz.getWorkbook().getActiveSheet();
    worksheetArray = sheet.getWorksheets();

    var filtersArray=[];
    var d=$.Deferred();

    (function runner(i){ 

        worksheetArray[i].getFiltersAsync().then(
        function(filtersArrayReturnedInPromise){

        for (z=0;z<filtersArrayReturnedInPromise.length;z++){
            field = filtersArrayReturnedInPromise[z].getFieldName();

            if (field.search("AutoFilter")>0 && field.search("Action")==-1 ){
                filtersArray[filtersArray.length]=field; 
            }

        }

        if (i==worksheetArray.length-1){
            var uniq = filtersArray.reduce(function(a,b){
            if (a.indexOf(b) < 0 ) a.push(b);
            return a;
                },[]);

        console.log("resolving filtersearch");
        d.resolve(uniq);

        } else {

            i++;
            runner(i);
        }

        });

    })(i)

return d.promise();
}
darth0s
  • 165
  • 2
  • 13
  • Since you are not using the promises' values as they arrive, why don't you just call [`Promise.all()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) and get all values when they all arrive? – 9000 May 17 '17 at 21:11
  • Use some polyfill for promises. Then you can use Promise.all in IE. – Tesseract May 17 '17 at 21:26
  • `I need to keep IE(Edge) compatibility so Promise.all() is not an option` promises wouldn't be an option at all if Promise.all isn't an option - clearly you must have some sort of Promise library loading for any promises to work at all – Jaromanda X May 17 '17 at 23:13
  • @SpiderPig right... Simple project turns out to be a headache.. Could you please point me in right direction to the proper library I should include? – darth0s May 18 '17 at 09:03

1 Answers1

0

please use/declare (local) variables and stop polluting the global namespace.

I've rewritten your code to properly deal with the promises.

filtersArrayFetch() has to return a promise so that other functions can deal with the result of its computation. And testu() needs to use this promise in order to determine when filtersArrayFetch is done.

Your code didn't wait for the promises to resolve, before trying to process/log the filtersArray. And your approach with the global/shared filtersArray that is modified in the promises is pretty dangerous/unpredictable itself. Take that pattern to a function that you call twice and all hell will break loose; you'll not only rip out your hair, it'll set your head on fire when trying to reproduce some result or debugging the mess that this can create.
Because most of your code will have no clue when some random promise will finish and start adding items to that array; nor what function call these items are coming from.

function testu(){
    filtersArrayFetch().then(filtersArray => {
        console.log("finished fetching");
        console.log(filtersArray);      
        //do more stuff with array
    });
}

function filtersArrayFetch() {
    //a few functions that define some tasks/steps that need to be done 
    //in order to do the whole task.

    //Array<Array<*>> -> Array<*>
    var flatten = arrays => [].concat(...arrays);

    //filter -> fieldName
    var getFieldName = filter => filter.getFieldName();

    //Array<filter> -> Array<fieldName>
    var filtersToFieldNames = filters => filters.map(getFieldName);

    //worksheet -> Promise<Array<fieldName>>
    var worksheetToFieldNames = worksheet => worksheet.getFiltersAsync().then(filtersToFieldNames);

    //telling by your code, to this point everything should be sync.
    var worksheets = viz.getWorkbook().getActiveSheet().getWorksheets();

    return Promise.all(worksheets.map(worksheetToFieldNames)).then(flatten);
}

worksheets.map(worksheetToFieldNames) transforms an Array<worksheet> into an Array<Promise<Array<fieldName>>>. After running this through Promise.all() we have a Promise<Array<Array<fieldName>>>. The promise of an Array of Arrays; for each worksheet an array of fieldnames.

And after running this through .then(flatten) we have a plain Promise<Array<fieldName>>.

But from the point we started to deal with promises we will have to continue dealing with promises. We cannot unwrap the values and get sync anymore. So the very only thing this function can return is a Promise (of whatever).

Thomas
  • 11,958
  • 1
  • 14
  • 23
  • can `Promise.all()` be replaced with `$.when()`? as I am using jQuery at least I would keep IE compatibility without adding next libraries ? – darth0s May 18 '17 at 09:05