Javascript 递归函数中的承诺
我有以下代码:Javascript 递归函数中的承诺,javascript,promise,es6-promise,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 = [].slic
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);
}
}
}
功能加载(实验室,el){
返回Promise.all([Util.loadHtml(lab)、Util.loadScript(lab)])
.然后(功能(响应){
解析(响应[0],el,响应[1]);
});
}
函数解析(html、父级、上下文){
var children=[].slice.call(html.childNodes).filter(函数(项){return item.nodeType==1 | | item.nodeType==3;});
对于(变量i=0;i=0){
加载(子标记名.toLowerCase(),父级);
}
否则{
var parsedNode=parseNode(子节点,上下文);
parent.appendChild(parsedNode);
if(child.hasChildNodes())
parse(子级、parsedNode、上下文);
}
}
}
基本上,这就是它应该做的:
load
函数,该函数将导入两个文件,一个html
和一个js
,当这些请求的承诺得到满足时,它将调用名为parse
的函数,该函数将循环到html中,并使用js文件中声明的类解析一些字符串
,然后,它将尝试加载my element.html
和my element.js
,并在该html中循环load
函数返回一个承诺,我同步调用它,它立即返回,因此,子项没有放在正确的父项中
例如,如果我在C#中这样做,或者使用ES7
async
和await
关键字,那将非常简单。但是我不知道如何异步调用load
函数。有什么猜测吗?我想这就是你弄错的地方:
if (child.tagName.indexOf('-') >= 0) {
load(child.tagName.toLowerCase(), parent);
}
您正在将
子对象的父对象
传递为孙对象的父对象
。您可能需要将子项
作为您可以使用的孙子项
的父项传递。减少以实现此目的:
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
get resolved返回。因此,您现在可以像这样使用它:
parse(THE PARAM OF ROOT).then(function() {
// All the big parents's children are now parsed.
console.log('All done');
});
编辑:正如建议的那样,这样更好。减少,因为当任何子(孙)失败时,它会立即拒绝。如前所述,我只提供一个指向它的链接,而不是添加.all
版本
如果函数是异步的,它应该返回一个承诺。总是甚至(或:特别是)在中,然后回调
如果您在该循环中产生多个承诺,您可以通过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]);
}
最后,这比我想象的要简单:
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);
}
}
}
功能加载(实验室,el){
返回Promise.all([Util.loadHtml(lab)、Util.loadScript(lab)])
.然后(功能(响应){
return parse(responses[0],el,responses[1]);//返回此处
});
}
函数解析(html、父级、上下文){
var children=[].slice.call(html.childNodes).filter(函数(项){return item.nodeType==1 | | item.nodeType==3;});
对于(变量i=0;i=0){
返回加载(child.tagName.toLowerCase(),parent);//在此处返回
}
否则{
var parsedNode=parseNode(子节点,上下文);
parent.appendChild(parsedNode);
if(child.hasChildNodes())
parse(子级、parsedNode、上下文);
}
}
}
由于我的解析必须是同步的(因为顺序等原因),我唯一需要做的就是等待load
函数完成,然后再返回parse
,因此我所做的唯一更改是不直接调用load
函数中的parse
函数,反之亦然,我现在使用返回
,因为它将等待执行,然后再返回调用方
还有一件事很管用,而且对我的用例更好:我最终创建了一个自定义元素的克隆,并将其附加到父元素,调用加载函数传递它。这样,我就可以异步加载它的所有子对象,而不会出现不连接到DOM的问题
执行速度更快,可读性更好 我看不出它是如何将其放入不正确的父对象中的,因为它是异步的。
。嗯,如果您也对文本节点进行筛选,您不应该在不检查它是否是元素的情况下访问它们的.tagName
。@Bergi事实上,我的代码比这个大得多。我已经简化了我的问题的上下文——我检查节点的类型等:)你应该真正考虑做<代码>返回承诺。所有([承诺,孩子承诺])< /COD>而不是这个<代码>。这样,它会立即拒绝出现错误。@Bergi,谢谢你的提醒,只是在写答案时忘记了。所有的!代码中没有“逻辑”错误。事实上,如果我没有使用任何自定义标记,它就像一个符咒,只有当我需要加载其他html时才会出现问题