简体   繁体   中英

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 Error is 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 ]

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);
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM