4

Here is my hot mess of a function. This will grab the model variants (which are in an array as strings) from the a csv file (the path of this file depends on text we grab from other csv files, hence the looping).

The csvService.getCsvAsArray call you see gets an object with the contents of a csv file where each column is stored in an array under an attribute with the name of the top column. That’s working fine so just know that when you see things like result["NavigationSectionShortNames"], it’s just an array of strings.

    var INDEX_OF_PRODUCTS_SECTION = 1;
    var getAllModelVariants = function () {
        return csvService.getCsvAsArray("Contents/Sections/" + navFileNames[INDEX_OF_PRODUCTS_SECTION] + "/order.csv").then(function (result) {
            var products = [];
            for (var i = 0; i < result["NavigationSectionShortNames"].length; i++) {
                csvService.getCsvAsArray("Contents/Sections/" + navFileNames[INDEX_OF_PRODUCTS_SECTION] + "/" + result["NavigationSectionShortNames"][i]
                + "/order.csv").then(function (productModelInfo) {
                    var productFamilies = []; //array of all different families for a certain product
                    for (var j = 0; j < productModelInfo["NavigationSectionShortNames"].length; j++) {
                        csvService.getCsvAsArray("Contents/Sections/" + navFileNames[INDEX_OF_PRODUCTS_SECTION].length + "/" + result["NavigationSectionShortNames"][i] + "/" + productModelInfo["NavigationSectionShortNames"][j] + "/order.csv").then(function (modelVariants) {
                            var productModels = []; //array of all different model types for a certain family
                            for (var k = 0; k < modelVariants.length; k++) {
                                productModels.push(modelVariants["NavigationSections"][k]);
                            };
                            productFamilies.push(productModels);
                        });
                    };
                    products.push(productFamilies);
                });

            };
        })
        return products;
    };

Obviously, this won't work because by the time the promise is resolved, the incrementing variable (i, j, or k) has already changed. Is there to use promises in nested for loops like these successfully? I have came across $q.all but I am struggling to figure out how to apply it to my function. I am currently looking at this example which uses promises in a single for loop, and I will try to expand on it.

My desire is to return a 3D array (see plunker at bottom of question for an example).

If it helps, here is a simplified version of this function with static data and no promises:

    var getAllModelVariantsTest = function () {
        var result = ["productFamily1", "productFamily2", "productFamily3"];
        var testArray = ["productFamilyModel1", "productFamilyModelt2", "productFamilyModel3", "productFamilyModel4"];
        var testArray3 = ["productFamilyModelVariant1", "productFamilyModelVariant2", "productFamilyModelVariant3", "productFamilyModelVariant4"];
            var products = [];
            for (var i = 0; i < result.length; i++) {
                    var productFamilies = []; //array of all different families for a certain product
                    for (var j = 0; j < testArray.length; j++) {
                            var productModels = []; //array of all different model types for a certain family
                            for (var k = 0; k < testArray3.length; k++) {
                                productModels.push(testArray3[k]);
                            };
                            productFamilies.push(productModels);
                    };
                products.push(productFamilies);
            };
            return products;
    };
    var testOutput = getAllModelVariantsTest();

Here it is in plunker form

If you run that you can see an example of what kind of output I'm desiring with my function above.

I'm wondering how I can get my first function with promises in nested for loops to work like the version in the plunker. Is there a way to do this with incrementing counter variables and promises?

Is $q.all the way to go?

Thank you very much for your time. Let me know if you need anything else or if I am being unclear.

Community
  • 1
  • 1
user95227
  • 1,853
  • 2
  • 18
  • 36

1 Answers1

2

Callback Hell or Pyramid of Doom solution:

function fatchAllDataForProduct(product) {

  var getProductFamilies = (product) => Promise.resolve(["productFamily1", "productFamily2", "productFamily3"]);
  var getFamilyModels    = (productFamily) => Promise.resolve(["productFamilyModel1", "productFamilyModelt2", "productFamilyModel3", "productFamilyModel4"]);
  var getModelVariants   = (familyModel) => Promise.resolve(["productFamilyModelVariant1", "productFamilyModelVariant2", "productFamilyModelVariant3", "productFamilyModelVariant4"]);
  var processVariant     = (modelVariant) => modelVariant;
  var mapFunArray = ( fun ) => (array) => array.map( fun ); // mapFunArray( e => e )( [1,2,3,4] )

  return getProductFamilies(product)
    .then( pfs => pfs
      .map( pf => getFamilyModels(pf)
        .then( fms => fms
          .map( fm => getModelVariants(fm)
            .then( mvs => mvs
              .map( processVariant )
            )
          )
        )
      )
    );
}

fatchAllDataForProduct('foobarProduct').then(
  results => {
    console.log(results);
  }
);

getFoos returns Promise.
We call then on it which returns another Promise.
Inside then we call foos.map(..getBars().then..) which returns array of promises.
Inside then we call bars.map(..getGoos.then..).
Goos promises in turn resolve to results.

What you get is (* means array):

productPromise:
  *familyPromise:
    *modelsPromise:
      *processedVariant

To get rid of Callback Hell problem you may use async/await features of EcmaScript7, available today with transpilers like Babel.

async function fatchAllDataForProduct(product) {

  var getProductFamilies = (product) => Promise.resolve(["productFamily1", "productFamily2", "productFamily3"]);
  var getFamilyModels    = (productFamily) => Promise.resolve(["productFamilyModel1", "productFamilyModelt2", "productFamilyModel3", "productFamilyModel4"]);
  var getModelVariants   = (familyModel) => Promise.resolve(["productFamilyModelVariant1", "productFamilyModelVariant2", "productFamilyModelVariant3", "productFamilyModelVariant4"]);
  var processVariant     = (modelVariant) => modelVariant;

  var pfs = await getProductFamilies(product);
  var promises = pfs.map( async(pf) => {
    var fms = await getFamilyModels(pf)
    return fms.map( async(fm) => {
      var mvs = await getModelVariants(fm);
      return mvs.map( processVariant )
    })
  });
  return Promise.all(promises);
}

fatchAllDataForProduct('foobar').then(
  results => {
    for(var promise of results) {
      promise.then(...)
    }
  }
);
ilyaigpetrov
  • 3,657
  • 3
  • 30
  • 46
  • Your problem is really intricate, I suggest you to use `Babel` with `async/await` features or other more production ready libraries. – ilyaigpetrov Oct 12 '15 at 06:28