尝试比较Chrome中的两个书签子树,我遇到了异步API调用的问题,以查询书签文件夹的子项。
function titleComparator (lhs, rhs) {
return lhs.title < rhs.title ? -1 : lhs.title > rhs.title ? 1 : 0;
}
// Return whether two bookmark trees have equal content
function compare(lhs, rhs) {
// Not equal if one is a bookmark and another is a folder
if (('url' in lhs) != ('url' in rhs))
return false;
// If both are bookmarks, compare url and title
if ('url' in lhs && 'url' in rhs)
return lhs.title == rhs.title && lhs.url == rhs.url;
// If both are folders, compare contents
chrome.bookmarks.getChildren(lhs.id, function (lhsChildren) {
chrome.bookmarks.getChildren(rhs.id, function (rhsChildren) {
if (lhsChildren.length != rhsChildren.length)
return false; // Want to return from compare()
lhsChildren.sort(titleComparator);
rhsChildren.sort(titleComparator);
for (var i = 0; i < lhsChildren.length; i++)
if (!compare(lhsChildren[i], rhsChildren[i])
return false; // Same here
return true; // Same here
});
});
}
如何在递归函数中处理JavaScript中的回调?
答案 0 :(得分:0)
你必须提供一个回调,以便异步地吐出答案。
你曾经写过return
语句的任何地方,callback
关闭,你应该标准化,而不是将它传递给你的回调。
function compareAsync(lhs, rhs, callback) {
//…
callback(false); return;
//…
callback(lhs.title == rhs.title && lhs.url == rhs.url); return;
//…
callback(false); return; // Want to return from compare()
//…
var finished = 0;
var whetherSuccess = true;
lhsChildren.forEach(function(iterand, index) {
compareAsync(iterand, rhsChildren[index], function(result) {
whetherSuccess &= result;
if (++finished === lhsChildren.length) {
callback(whetherSuccess);
}
});
});
}
所以:在找到lhsChildren是什么时,我们开始了一堆异步函数。他们会在某个时刻递增finished
个计数器。他们每个人都有能力通过whetherSuccess
将整体false
降级为&=
。发现他们是获得答案的最终功能的消费者是将whetherSuccess
报告给原始callback
的人。
答案 1 :(得分:0)
您需要重构代码。
不知何故,似乎在异步(通常是延迟的)函数中使用递归来搜索基于树的或分层数据模型的方法不正确。
我认为这应该是这样做的方法:
请参阅我未经测试的代码以显示我的意思:
function compare(lhs, rhs, callback, index, lhsChilds, rhsChilds){
// Not equal if one is a bookmark and another is a folder
if (('url' in lhs) != ('url' in rhs)) {
callback(false);
return;
}
// If both are bookmarks, compare url and title
if ('url' in lhs && 'url' in rhs) {
callback(lhs.title == rhs.title && lhs.url == rhs.url);
return;
}
// If both are folders, check parameters and compare contents
//First, check if the list has already been loaded (code is running inside a recursive step)
if(lhsChilds != undefined && rhsChilds != undefined){
compareTwoChilds(lhs, rhs, callback, index, lhsChilds, rhsChilds);
}
else{
index = 0; //first recursion for this tuple (lhs, rhs)
chrome.bookmarks.getChildren(lhs.id, function (lhsChildren) {
chrome.bookmarks.getChildren(rhs.id, function (rhsChildren) {
compareTwoChilds(lhs, rhs, callback, index, lhsChilds, rhsChilds);
});
});
}
}
function compareTwoChilds(lhs, rhs, callback, index, lhsChilds, rhsChilds){
if (index < lhsChildren.length){ //just for the safety
if (lhsChildren.length != rhsChildren.length) {
callback(false);
return;
}
lhsChildren.sort(titleComparator);
rhsChildren.sort(titleComparator);
//compare using recursion, with an emtpy lists of rhs and lhs children
compare(lhsChildren[index], rhsChildren[index], function(compareResult){
if(!compareResult){
callback(false); //if the result is false, no more search needed
}else{ // use recursion again to loop through the next childs using the already loaded childs
if (++index < lhsChildren.length){
compare(lhsChildren[index], rhsChildren[index], callback, index, lhsChilds, rhsChilds)
}else{
callback(true); // the loop has ended,
}
}
});
}else{
callback(false); //this should never happen, so not the same...
}
}
您可以像这样调用比较函数:
compare(lhs,rhs, function(result){
var compareResult = result;
//carry on with your code here
});
//and not here :-)
答案 2 :(得分:0)
首先,我发现Chrome还具有getSubTree()
功能,这使得事情变得相当容易。因此,如果您只是想让它工作,请使用它而不是异步地逐节点遍历树节点。但是,这仍然是一个有趣的问题,感谢a reddit user,我找到了一个有效的解决方案。
compare()是递归调用自身的主函数。但是,由于内部的异步调用,它不能消耗它的递归调用的返回值。
// Pass a boolean to the callback indicating whether the recursive contents of
// both bookmarks folders are equal.
function compare(lhs, rhs, callback) {
// Compare titles except for the top-level folder
if (lhs.parent_ && lhs.title !== rhs.title) {
compare_failure(callback);
return;
}
// Compare urls if at least one of the sides is a bookmark
if ('url' in lhs || 'url' in rhs) {
if ((lhs.url || null) === (rhs.url || null))
compare_respond(lhs.parent_, callback);
else
compare_failure(callback);
return;
}
// For both sides being folders, we have to take a look at the contents
chrome.bookmarks.getChildren(lhs.id, function (lhs_children) {
chrome.bookmarks.getChildren(rhs.id, function (rhs_children) {
// Compare amount of children
if (lhs_children.length != rhs_children.length) {
compare_failure(callback);
return;
}
// Keep track of how many children already reported back
lhs.all_children = lhs_children.length;
lhs.equal_children = 0;
// Let pairs of children compare each other
lhs_children.sort(bookmark_comparator);
rhs_children.sort(bookmark_comparator);
for (var i = 0; i < lhs_children.length; i++) {
var lhs_child = lhs_children[i];
var rhs_child = rhs_children[i];
// Store parent reference so the deeper function can
// asynchronously respond with the results once finished.
lhs_child.parent_ = lhs;
compare(lhs_child, rhs_child, callback);
}
});
});
};
compare_respond()是用于传播更深层节点结果的对应物。它在上面的主函数中用来代替返回。
// Report comparison results back to the parent node. The parent node waits
// until it collected the results from all its children. Then it reports to
// its parent in turn. At the root node, the user callback is executed.
function compare_respond(node, callback) {
// Collect child results
node.equal_children++;
// Respond upwards if we got results from all
if (node.equal_children == node.all_children) {
if ('parent_' in node)
compare_respond(node.parent_, callback);
else
callback(true);
}
};
当我们找到一对不相等的节点时,compare_failure()用于在任何时候中止整个事情。在这种情况下,我们不必向上报告。
// Break out of the recursive function and report failure to the user. It's
// safe against being called multiple times so multiple children can report
// failure and the user will only be notified once.
function compare_failure(callback) {
if ('called' in callback)
return;
callback.called = true;
callback(false);
};
bookmark_comparator()是一个小助手,用于对子书签数组进行排序。需要排序来比较两个文件夹的内容,因为我不想依赖项目顺序。
// Comparator to sort lists of bookmark nodes first by title and second by
// url. Take into that folders have to url.
function bookmark_comparator(lhs, rhs) {
if (lhs.title != rhs.title)
return lhs.title < rhs.title ? -1 : 1;
if (lhs.url || null != rhs.url || null)
return lhs.url || null < rhs.url || null ? -1 : 1;
return 0;
};