【问题标题】:Javascript recursive function - call to asynchronous function at the leave levelJavascript递归函数 - 在休假级别调用异步函数
【发布时间】:2025-12-31 15:30:07
【问题描述】:

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

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

如果是,则返回集合

如果不是,则返回一个新的Error

问题是verySlowMan() 花时间检索一个集合。

为简单起见,我做了一个简单的if else 条件,但verySlowMan 最终会发出一个XHR 请求。所以速度出乎意料地慢

问题如下

如何将verySlowMan 的返回值一直传播到递归调用中,以通过调用recurse(prog) 获得一个不错的集合列表?

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

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

(1) 我不知道如何正确地做到这一点

(2) 我怀疑这是最好的方法

注意:progcollections 中的项目数量以及 recurse() 中的案例可能会变得很长。

这是程序:

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 ]

【问题讨论】:

  • 您的样本的预期输出是什么?似乎总是应该throw new Error
  • 当你在forEach回调中返回时 - 它不会从forEach运行的函数返回
  • 你将不得不使用回调,并且整个递归函数将在回调中返回结果,如果这适合你的用例
  • 在您的示例中,verySlowMan 不是异步的。这使您的代码令人困惑。另外,您想使用普通回调还是使用 Promise?如果内部也调用异步代码,则需要以异步样式编写所有循环和递归函数调用。

标签: javascript asynchronous recursion


【解决方案1】:

这是一个带有承诺的例子。

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

【讨论】:

  • 据我了解,您使用的是 ES6 中的 Promise。我可以对 Q 做同样的事情吗?
  • 好吧,使用 Q 几乎是一样的。但我使用 return Q.Promise(function(resolve, reject) 而不是 return new Promise(function(resolve, reject) 非常感谢您的帮助