递归函数内的promise

时间:2015-08-31 14:27:02

标签: javascript promise es6-promise

我有以下代码:

function load(lab, el) {
  return Promise.all([Util.loadHtml(lab), Util.loadScript(lab)])
    .then(function(responses) {
      parse(responses[0], el, responses[1]);
    });
}

function parse(html, parent, context) {
  var children = [].slice.call(html.childNodes).filter(function (item) { return item.nodeType === 1 || item.nodeType === 3; });

  for (var i = 0; i < children.length; i++) {
    var child = children[i];

    if (child.tagName.indexOf('-') >= 0) {
      load(child.tagName.toLowerCase(), parent);
    }
    else {
      var parsedNode = parseNode(child, context);
      parent.appendChild(parsedNode);
      if (child.hasChildNodes())
        parse(child, parsedNode, context);
    }
  }
}

基本上,这应该是它应该做的:

  1. 在我的app.js中,我调用load函数,该函数将导入两个文件,一个html和一个js,当这些请求的承诺完成后,它会调用一个名为parse的函数,它将循环到HTML中,并将使用JS文件中声明的类解析一些字符串。
  2. 在循环内部,可以找到一些自定义标记,例如<my-element>,然后,它会尝试加载my-element.htmlmy-element.js,然后它会在该HTML内部循环太
  3. 正如您在代码中看到的那样,我传递了父级和上下文,因此在完成所有循环后,“大”父级应该包含其中的所有其他组件。
  4. 问题

    由于load函数返回一个promise,而我正在同步调用它,它会立即返回,因此,子项不会放在正确的父项中。

    例如,如果我在C#中执行此操作,或使用ES7 asyncawait关键字,那将非常简单。但我不知道如何异步调用load函数。任何猜测?

4 个答案:

答案 0 :(得分:3)

如果函数是异步的,它应该返回一个promise。总是。甚至(或者:尤其)在then回调中。

如果您在该循环中产生多个承诺,则可以通过Promise.all

等待它们
function load(lab, el) {
  return Promise.all([Util.loadHtml(lab), Util.loadScript(lab)])
    .then(function(responses) {
      return parse(responses[0], el, responses[1]);
//    ^^^^^^
    });
}

function parse(html, parent, context) {
  var children = [].slice.call(html.childNodes).filter(function (item) { return item.nodeType === 1 || item.nodeType === 3; });

  return Promise.all(children.map(function(child, i) {
//^^^^^^^^^^^^^^^^^^

    if (child.tagName.indexOf('-') >= 0) {
      return load(child.tagName.toLowerCase(), parent);
//    ^^^^^^
    } else {
      var parsedNode = parseNode(child, context);
      parent.appendChild(parsedNode);
      if (child.hasChildNodes())
        return parse(child, parsedNode, context);
//      ^^^^^^
    }
  }));
}
  

如果我在C#中执行此操作,或者使用ES7 async和await关键字,那将非常简单。但我不知道如何异步调用load函数

是的,你真的应该考虑使用它们。或者您可以使用ES6生成器函数和运行器(由许多流行的promise库提供)来模拟它们。但无论如何你都在使用转换器,对吗?

load对他们来说很容易:

async function load(lab, el) {
  var responses = await Promise.all([Util.loadHtml(lab), Util.loadScript(lab)]);
  return parse(responses[0], el, responses[1]);
}

答案 1 :(得分:1)

您可以使用.reduce来实现:

$form = $this->get('form.factory')->createNamedBuilder('your-custom-name', 'form',  null, array(
    'constraints' => $collectionConstraint,
))
->add('delete', 'submit')
->getForm();

然后,当通过孩子的角蛋白时,它会继续链接承诺,每个孩子可以独立加载或解析,直到所有孩子都得到解决,从function load(lab, el) { return Promise.all([Util.loadHtml(lab), Util.loadScript(lab)]) .then(function(responses) { return parse(responses[0], el, responses[1]); }); } function parse(html, parent, context) { var children = [].slice.call(html.childNodes).filter(function (item) { return item.nodeType === 1 || item.nodeType === 3; }); // What this return is a promise chained through all of its children // So until all children get resolved, this won't get resolved. return children.reduce(function(promise, child, idx) { var childPromise = null; if (child.tagName.indexOf('-') >= 0) { // Start to load the contents, until childPromise is resolved, the final // can't be resolved. childPromise = load(child.tagName.toLowerCase(), parent); } else { var parsedNode = parseNode(child, context); parent.appendChild(parsedNode); // If it has child, also make it return a promise which will be resolved // when child's all children parsed. if (child.hasChildNodes()) { childPromise = parse(child, parsedNode, context); } } // It's either null, which means it'll be resolved immediately, // or a promise, which will wait until its childs are processed. return promise.then(function() { return childPromise; }); }, Promise.resolve()); } 返回的承诺得到解决。所以你现在可以使用它:

parse

编辑:正如Bergi所暗示的那样,Promise.all比.reduce更好,因为它会在任何一个孩子(孙子女)失败时立即拒绝。在发布The more acceptable answer时,我只是给它一个链接,而不是添加parse(THE PARAM OF ROOT).then(function() { // All the big parents's children are now parsed. console.log('All done'); }); 版本。

而且JavaScript Promises#chaining也可以帮到你。

答案 2 :(得分:0)

我认为这是你弄错了:

if (child.tagName.indexOf('-') >= 0) {
      load(child.tagName.toLowerCase(), parent);
    }

您正在将parent对象的child传递给parent对象的grandchild。您可能需要将child作为grandchild的父级传递。

答案 3 :(得分:0)

最后,它比我想象的要简单:

function load(lab, el) {
  return Promise.all([Util.loadHtml(lab), Util.loadScript(lab)])
    .then(function(responses) {
      return parse(responses[0], el, responses[1]); // return here
    });
}

function parse(html, parent, context) {
  var children = [].slice.call(html.childNodes).filter(function (item) { return item.nodeType === 1 || item.nodeType === 3; });

  for (var i = 0; i < children.length; i++) {
    var child = children[i];

    if (child.tagName.indexOf('-') >= 0) {
      return load(child.tagName.toLowerCase(), parent); // return here
    }
    else {
      var parsedNode = parseNode(child, context);
      parent.appendChild(parsedNode);
      if (child.hasChildNodes())
        parse(child, parsedNode, context);
    }
  }
}

由于我的解析必须是同步的(因为订单等),我唯一需要的是等待load函数完成后再回到parse,我改变的唯一的事情是的,而不是直接调用parse内的load函数,反之亦然,我现在正在使用return,从那时起它将等待执行,之后回到来电者那里。

其他有用的东西,对我的用例来说甚至更好:我最终创建了我的自定义元素的克隆,并将其附加到父级,调用load函数传递它。这样做,我可以加载它的所有子异步,而不会没有附加到DOM。

执行速度更快,可读性更高!