我有以下方法:
function getRelevantArticles(amount,
userSuggestions,
suggestionPage,
relevantArticles,
continueFlag,
success,
error)
{
if(continueFlag)
{
getSuggestedArticles(suggestionPage, userSuggestions, function (articles)
{
if(articles.length == 0)
getRelevantArticles(amount, userSuggestions, suggestionPage, relevantArticles, false, success, error); // continueFlag= false
getUnvisitedArticles(articles, function (unvisited)
{
for(var i = 0; i < unvisited.length; i++)
relevantArticles.push(unvisited[i]);
relevantArticles= filterRelevant(amount, userSuggestions, relevantArticles);
if(relevantArticles.length < amount)
getRelevantArticles(amount, userSuggestions, suggestionPage + 1, relevantArticles, true, success, error); // continueFlag= true
else
getRelevantArticles(amount, userSuggestions, suggestionPage, relevantArticles, false, success, error); // continueFlag= false
}, error);
}, error);
}
else if(success)
{
fillWithContent(relevantArticles, success, error); //Should be last method to execute
}
}
上下文
我知道可能很难理解并且可以对其进行很多优化,我将尽力解释它的作用(或试图做到):
首先使用标志
continueFlag= true
调用该方法,因此首先调用getSuggestedArticles
,这是一个发出AJAX请求的异步方法。我将请求结果传递给回调函数。getSuggestedArticles
为我提供了与用户建议有关的文章ID。 (用户建议是用户可能感兴趣的主题列表)。我输入
suggestionPage
是因为建议可能很多,而且我应该只有几篇(第一页)就能获得相关的文章。如果未检索到任何文章,则表明我们没有建议(每个建议至少都有一篇文章),即。例如,我们到达最后一页,因此我们将
continueFlag
标志设置为false
,以调用finalizer方法。如果至少有一篇文章,我将调用
getUnvisitedArticles
,这是另一个发出AJAX请求的异步方法。这种方法为我提供了用户没有访问或阅读的文章,而这些正是我所关心的。我有一个
relevantArticles
变量,该变量跟踪我发现相关的文章并将呈现给用户。从当前未访问文章的页面中获取相关文章并将其附加到上一页的相关文章之后,我将检查是否有显示的文章最低数量amount
。如果我还不满足最低要求,那么我继续下一页(
suggestionPage + 1
);如果达到最小阈值,则继续使用终结器方法(
continueFlag= false
)fillWithContent
是当我完成识别相关文章时将调用的方法。这是一个异步方法,它将发出AJAX请求,并用其他信息填充我的article对象。getSuggestedArticles(编号:建议页面,数组:userSuggestions,功能:成功,功能:错误)
接收用户建议数组,并获取此数组的第n页(页面大小100)。
假设我们这样调用方法:
getSuggestedArticles(0, [ 745, 4567, 1500 ], function (data) {
var articles = data;
}, error);
该方法向数据库Web API发出请求,并将建议的文章数组传递给
success
函数。在前面的示例中,articles
变量将具有这样的数组(请注意,所有返回的建议文章在其主题中至少有一个用户建议):[
{
id: 12345,
topics: [ 998, 1500, 323 ] //has user suggestion 1500
},
{
id: 45778,
topics: [ 009, 1500, 745] //Has user suggestion 745 and 1500
},
...
]
getUnvisitedArticles(数组:文章,功能:成功,功能:错误)
接收一系列文章,并返回所有用户未访问过的文章。
假设我们这样调用此方法:
//We are using the same "articles" variable from the previous example
getUnvisitedArticles(articles, function (data) {
var unvisited = data;
}, error);
该函数向Database Web Api发出请求,并将包含未访问文章的数组传递给
success
函数。在前面的示例中,变量unvisited
将具有如下数组:[
{
id: 45778,
topics: [ 009, 1500, 745]
}
]
请注意,编号为12345的文章已消失。这是因为用户已经访问过它。
fillWithContent(Array:relatedArticles,Function:success,Function:error)
接收一系列文章,并使用其他信息填充这些对象。
假设我们这样调用此方法:
//We are using the same "unvisited" variable from the previous example
fillWithContent(unvisited, function (data) {
filledArticles = data;
}, error);
该函数向Database Web Api发出请求,并将带有填充文章的数组传递给
success
函数。在前面的示例中,变量filledArticles
将具有如下数组: [
{
id: 45778,
topics: [009, 1500, 745],
title: 'Article title',
publicationDate: 'Some date',
author: 'Some author',
...
}
]
这是我的调用者正在使用的数组,调用者是指调用我的
getRelevantArticles
函数的那个数组。问题
此方法的问题是
fillWithContent
被无限调用,因此导致发出大量请求,浏览器崩溃,并出现递归溢出。我不是从另一个地方调用此方法的,因此此函数必须存在问题。
我写了一个
console.log(suggestionPage)
,看来它也不断地无限增加变量。它应该已经在页面3
处停止了,因为articles.length == 0
。但这并没有停止。这里发生了什么?
最佳答案
我认为您应该将工作负载分为独立的部分,这些部分很容易解释,并且可以轻松组合以得到更复杂的结果。
据我了解,这项工作包括三个基本部分,全部通过Ajax完成:
[主题ID]到[这些主题的文章存根](getSuggestedArticles)
[文章存根]到[相关(未读)文章存根](getUnvisitedArticles)
[文章存根]到[全文](fillWithContent)
所有这些Ajax请求都应以分页方式进行,例如1000个项目由10个请求处理,每个请求100个项目。
为此,我们定义了一个实用程序函数,该函数接受项目列表,对它们进行分页,对每个页面进行Ajax请求(通过我们作为参数传递的辅助函数)并返回所有请求的组合结果。
// utility: runs an ajax request and handles errors on the low level
function ajax(options) {
return $.ajax(options).fail(function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown, jqXHR);
});
}
// utility: makes paged Ajax requests through a worker function
function pagedAjax(ajaxFunc, items, pageSize) {
var temp = [].slice.call(items), page,
requests = [];
// start as many parallel Ajax requests as we have pages
while (temp.length) {
page = temp.splice(0, pageSize);
requests.push( ajaxFunc(page) );
}
// wait until all requests have finished, return combined result
return $.when.apply($, requests).then(function (results) {
var combined = [];
$.each(results, function (i, result) {
// result is an array [data, textStatus, jqXhr]
// push all contained items onto combined
// (the following assumes that data is an array of objects)
[].push.apply(combined, result[0]);
});
return combined;
});
}
现在,我们可以设置三个工作程序功能。它们接受任意数量的输入,因为所有分页都由上述实用程序功能完成:
// worker: retrieves a list of article IDs from topic IDs
function getSuggestedArticles(topics) {
return ajax({method: 'post', url: '/articlesByTopic', data: topics});
// or whatever API request returns a list of articles IDs from topic IDs
}
// worker: takes a list of article IDs, returns a list of _unread_ article IDs
function getUnvisitedArticles(articles) {
return ajax({method: 'post', url: '/unvisitedArticles', data: articles});
// or whatever API request returns a list of unvisited articles from IDs
}
// worker: takes a list of article IDs, returns a list of articles
function fillWithContent(articles) {
return ajax({method: 'post', url: '/articles', data: articles});
// or whatever API request fills articles with content
}
之后,组合功能就不再困难了:
// takes a list of topic IDs, requests article IDs, filters them, returns actual articles
function getUnvisitedArticlesByTopic(topicIds) {
var pageSize = 100;
return pagedAjax(getSuggestedArticles, topicIds, pageSize)
.then(function (allArticles) {
return pagedAjax(getUnvisitedArticles, allArticles, pageSize);
})
.then(function (unvisitedArticles) {
return pagedAjax(fillWithContent, unvisitedArticles, pageSize);
});
}
我们可以通过一个非常简单的调用来使用它:
// renders unvisited articles
function renderUnvisitedArticles() {
var topicIds = [9, 1500, 745];
getUnvisitedArticlesByTopic(topicIds).done(function (articles) {
$.each(articles, function (i, article) {
// show article on page
});
});
}
这种基于承诺的方法的好处:
没有回调地狱。
简短的功能只能做一件事。
没有自呼功能。
各个零件具有良好的可重用性和可测试性。
推荐阅读的当然是jQuery的Deferred objects文档。
免责声明:该代码确实未经测试。如果发现错误,请告诉我。
关于javascript - 带有标志变量的递归和异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31835492/