首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >带有标志变量的递归和异步方法

带有标志变量的递归和异步方法
EN

Stack Overflow用户
提问于 2015-08-05 14:33:00
回答 1查看 166关注 0票数 0

我有以下方法:

代码语言:javascript
运行
复制
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为我提供了与用户建议相关的文章Ids。(用户建议是用户可能感兴趣的主题列表)。

我通过了一个suggestionPage,因为建议可能很多,我应该能够得到相关的文章只有几个(第一页)。

如果没有检索到文章,那么就意味着我们没有建议(每个建议至少有一篇文章),也就是说,我们到达了最后一页,因此我们将continueFlag标志设置为false,以调用终结器方法。

如果至少有一篇文章,我将调用getUnvisitedArticles,这是另一种发出AJAX请求的异步方法。这个方法给出了用户没有访问或阅读的文章,这些都是我关心的文章。

我有一个relevantArticles变量,它跟踪我发现的相关文章,并将提供给用户。一旦我从我当前未访问过的文章中获得了相关的文章,并将它们附加到上一页的相关文章中,我就会检查我是否有要显示的文章的最小amount

如果我还没有满足最小数量,那么我继续下一页(suggestionPage + 1);

如果达到最小阈值,则继续使用终结器方法(continueFlag= false)。

fillWithContent是在我识别完相关文章之后将被调用的方法。它是一种异步方法,它将发出AJAX请求,并将补充信息填充我的文章对象。

getSuggestedArticles(编号: suggestionPage,数组: userSuggestions,函数:成功,函数:错误)

接收用户建议数组,并接受该数组的第n页(页大小100)。

让我们假设我们这样调用这个方法:

代码语言:javascript
运行
复制
getSuggestedArticles(0, [ 745, 4567, 1500 ], function (data) {
      var articles = data;
}, error);

该方法向Database发出请求,并将建议的文章数组传递给success函数。在前面的示例中,articles变量将有一个类似于此的数组(请注意,所有返回的建议文章在其主题中至少有一个用户建议):

代码语言:javascript
运行
复制
[
    {
         id: 12345,
         topics: [ 998, 1500, 323 ] //has user suggestion 1500
    },
    {
         id: 45778,
         topics: [ 009, 1500, 745] //Has user suggestion 745 and 1500
    },
    ...
]

getUnvisitedArticles(数组:文章,函数:成功,函数:错误)

接收一组文章,并返回所有未被用户访问的文章。

让我们假设我们这样调用这个方法:

代码语言:javascript
运行
复制
//We are using the same "articles" variable from the previous example
getUnvisitedArticles(articles, function (data) {
     var unvisited = data;
}, error); 

该函数向Database发出请求,并将一个带有未访问项目的数组传递给success函数。在前面的示例中,变量unvisited具有如下所示的数组:

代码语言:javascript
运行
复制
[
      {
           id: 45778,
           topics: [ 009, 1500, 745]
      }
]

注意,id 12345的文章已经消失。这是因为用户已经访问了它。

fillWithContent(数组: relevantArticles,函数:成功,函数:错误)

接收一组项目,并使用其他信息填充这些对象。

让我们假设我们这样调用这个方法:

代码语言:javascript
运行
复制
 //We are using the same "unvisited" variable from the previous example
 fillWithContent(unvisited, function (data) {
     filledArticles = data;
 }, error);

该函数向Database发出请求,并向success函数传递一个包含已填充文章的数组。在前面的示例中,变量filledArticles具有如下所示的数组:

代码语言:javascript
运行
复制
 [
       {
           id: 45778,
           topics: [009, 1500, 745],
           title: 'Article title',
           publicationDate: 'Some date',
           author: 'Some author',
           ...
       }
 ]

--这是我的调用方正在驱逐的数组,调用者我指的是调用我的getRelevantArticles函数的数组。

问题所在

这个方法的问题是,fillWithContent 被无限地调用,从而导致大量请求被发出,浏览器崩溃,以及递归溢出产生。

我不是从另一个地方调用这个方法,所以它必须是这个函数的一个问题。

我写了一个console.log(suggestionPage),它似乎也在无止境地递增变量。它应该在页面3停止,因为articles.length == 0。但它并没有停止。

这里发生什么事情?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-08-05 17:56:59

我认为您应该将您的工作负载分成一些独立的部分,这些部分很容易解释,也可以很容易地结合起来,以获得更复杂的结果。

据我了解,这项工作由三个基本部分组成,所有这些部分都是通过Ajax完成的:

  1. 主题ID到这些主题的文章存根
  2. 文章存根到(未读)文章存根
  3. 文章存根到全文

所有这些Ajax请求都应该以分页的方式进行,例如,由10个请求处理1000个项目,每个请求100个条目。

为此,我们定义了一个实用函数,它接收项目列表,分页,为每个页面发出Ajax请求(通过我们作为参数传递的worker函数),并返回所有请求的合并结果。

代码语言:javascript
运行
复制
// 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;
    });
}

现在,我们可以设置我们的三个工人的功能。它们接受任意数量的输入,因为所有分页都是由上面的实用程序函数完成的:

代码语言:javascript
运行
复制
// 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
}

在此之后,合并这些功能不再困难:

代码语言:javascript
运行
复制
// 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);
        });
}

我们可以通过一个非常简单的调用来使用它:

代码语言:javascript
运行
复制
// 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关于延迟对象的文档。

免责声明:代码确实未经测试。如果你发现了错误,告诉我。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31835492

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档