Javascript递归函数-在左级调用异步函数

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

本文关键字:调用 异步 函数 递归函数 Javascript      更新时间:2023-09-26

在下面的程序中,当最初调用recurse(prog)(第64行)时,它递归地挖掘prog(第1行)中描述的表达式exp,在exp.type = A, B或c之间切换。

在递归调用的最底层(当case("C")),我调用查找collections列表的verySlowMan(exp.value),以检查具有exp.value名称的集合是否存在。

如果是,则返回集合

如果没有,则返回一个新的Error

问题是verySlowMan()花了他的时间来检索集合。

为简单起见,我设置了一个简单的if else条件,但verySlowMan最终将发出XHR请求。所以它是不可预测的慢

问题如下:

如何将verySlowMan的返回值一直传播到递归调用,以获得一个很好的集合列表作为调用recurse(prog)的结果?

目前,由于可以理解的原因,我得到[null, [null, null], null]。但是我真的不知道如何解决它。

我试图返回一个延迟的。从verySlowMan的承诺,但在这种情况下,我认为recurse()也应该为每个递归调用返回一个新的承诺。

(1)我不确定如何使这个正确

我怀疑这是不是最好的办法

注意:prog, collections和recurse()中的case中的项目数量可能会变得非常长。

程序如下:

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 ]

下面是一个带有承诺的例子。

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