Mikou
Mikou

Reputation: 651

Javascript recursive function - call to asynchronous function at the leave level

In the following program, when recurse(prog) is initially called (line 64), it recursively digs into the expressions exp described in prog (line 1), switching between the case where exp.type = A, B or C.

At the bottom most level of the recursive call (when case("C")), I am calling verySlowMan(exp.value) that looks up a list of collections to check whether a collection with the name of exp.value exists or not.

If yes, the collection is returned

If no, a new Erroris return

The problem is that verySlowMan() take his time to retrieve a collection.

For simplicity, I made a simple if else condition, but verySlowMan will ultimately make an XHR request. So it is unpredictably slow

The question is the following :

How to propagate the return value of verySlowMan all the way up the recursive call to get a nice list of collections as a result of calling recurse(prog) ?

Currently, for understandable reasons, I get [ null, [ null, null ], null ]. But I have really no clue about how to solve it.

I tried to return a deferred.promises from verySlowMan, but in this case I think recurse() should also return a new promise for each recursive call.

(1) I'm not sure about how to make this properly

(2) I doubt it's the best way

NB : The amount of items in prog, collections and the cases in recurse() can potentially become very long.

Here is the program :

var prog = {
  type : "A", value : [
    { type : "B", value : "C1" },
    { type : "B", value : [
      { type : "C", value : "C2" },
      { type : "C", value : "end" }
    ]},
    { type : "B", value : "C3" }
  ]
}

var collections = [
  { name : "C1", data : ["item1", "item2", "item3"]},
  { name : "C2", data : ["item1", "item2", "item3"]}
]

function verySlowMan( collectionToFind ){

  collections.forEach(function(collection){

    if ( collection.name === collectionToFind ) {
      return collection;
    }else{
      return new Error("No Collection");
    }
  });

  return null;
}

function recurse(exp){

  switch(exp.type){
    case("A"):
      var As = [];
      exp.value.forEach( function(B){
        As.push ( recurse(B) );
      } );
      return As;
      break;

    case("B"):
      var Bs = [];
      if (typeof(exp.value) === 'string') {
        return verySlowMan( exp.value );

      } else {
        exp.value.forEach( function(C){
          Bs.push ( recurse(C) );
        } );
        return Bs;
      }
      break;

    case("C"):
      return verySlowMan( exp.value );
      break;

    default:
      throw new Error('wrong type');
  }
}

console.log( recurse(prog) ); // -> [ null, [ null, null ], null ]

Upvotes: 0

Views: 110

Answers (1)

Tesseract
Tesseract

Reputation: 8139

Here is an example with promises.

function verySlowMan( collectionToFind ) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      for(var i = 0; i < collections.length; i++) {
        if ( collections[i].name === collectionToFind ) {
          resolve(collections[i]);
          return;
        }
      }
      reject(new Error("No Collection"));
    }, 100);
  });
}

function recurse(exp){
  function errorHandler(err) {
    if(err.message === "No Collection") return null;
    else throw err;
  };
  switch(exp.type){
    case("A"):
      return Promise.all(exp.value.map(recurse));
    case("B"):
      if (typeof(exp.value) === 'string') {
        return verySlowMan(exp.value).catch(errorHandler);
      } else {
        return Promise.all(exp.value.map(recurse));
      }
    case("C"):
      return verySlowMan( exp.value ).catch(errorHandler);

    default:
      return Promise.reject(new Error('wrong type'));
  }
}

recurse(prog).then(function(result) {
  console.log(result);
}).catch(function(err) {
  console.log(err.stack);
});

Upvotes: 1

Related Questions