无法使用$ .Deferred()对象和$ .then()来中断递归

时间:2014-04-11 16:49:00

标签: javascript jquery recursion jquery-deferred jquery-1.7

我必须搜索可能有数十万行的单词索引表。我可以通过将文档列表传递给搜索来限制我的搜索。请求在许多文档中搜索单词的速度非常慢。因此......为了改进用户体验,我们将请求分成几组文档。因此,如果用户要求搜索90个文档,并且每个查询的块大小为10个文档,那么我们发出90/10 = 9个独立的$ .ajax()调用。我们希望结果按发送顺序排列。

我们实现了这个递归:

var SearchFunction = function () {
   $.ajax(/* ... */);
   }

var RecursiveSearch = function () {
   var deferred = $.Deferred();
   if (arrTransSearch.length > 0) {
      deferred = SearchDocuments(arrTransSearch.shift());
   }
   else {
      deferred.reject();
   }

   return deferred.promise().then(RecursiveSearch);
}

if (arrTransSearch.length > 1) {
   RecursiveSearch().fail(SomeFunction);
}

var SomeFunction = function () {
   alert("Failed. Yes!");
}

当我调试代码时,显示 deferred.reject()不会更改deferred.promise()的状态。也就是说,当下一行

return deferred.promise().then(RecursiveSearch)
执行

,它只是循环回到递归函数,而不是退出递归并落入

RecursiveSearch().fail(SomeFunction);

重要提示:

我正在使用 jQuery-1.7.1 。我在JSFiddle中运行了类似的递归(谢谢Beeetroot-Beetroot),但在 jQuery-2.1.0 上失败了,而在 jQuery-2.1.0 上跑了没问题。

如何让递归在 jQuery-1.7.1 中运行?

2 个答案:

答案 0 :(得分:2)

在“The Collection Kerfuffle”标题下提供了一个部分涵盖您所寻找内容的模式 here 。实际上你需要稍微多一些,因为你想要以块(组)的形式处理你的文档引用列表。

代码将是这样的:

$(function() {

    //General ajax options for searching a document group
    var ajaxOptions = {
        url: '...',
        type: 'POST',
        //data: ... //added dynamically 
        dataType: 'JSON',
        // etc.
    };

    //
    function searchDocumentsInGroups(arr, n) {
        //Pre-process arr to create an array of arrays, where each inner array is a group of document references
        var groups = [];
        $.each(arr, function (i) {
            if (!(i % n)) groups.push(arr.slice(i, i + n));
        });

        //Ajax serializer (from the Collection Kerfuffle reference)
        return groups.reduce(function (promise, group) {
            return promise.then(function () {
                return $.ajax($.extend({}, ajaxOptions, {
                    data: JSON.stringify(group);//or whatever, compatible with the server-side script
                })).then(function (groupResults) {
                    //display groupResults here
                });
            });
        }, $.when(0));
    }

    // data
    var myDocumentArray = [ 'doc1', 'doc2', 'doc3', 'doc4', 'etc.' ], //Your array of 90 document references.
        groupSize = 10; //Number of documents per "chunk".

    // Event handler to kick off the process.
    $("#searchDocuments").on('click', function () {
        // display "in progress" message or spinner here
        searchDocumentsInGroups(myDocumentArray, groupSize).then(function () {
            // display "complete" message or hide spinner here
        });
    });
});

你还需要Polyfill for Array.prototype.reduce,因为上面依赖.reduce而旧的浏览器(ECMAScript5之前版本)没有它。

if ( 'function' !== typeof Array.prototype.reduce ) {
  Array.prototype.reduce = function( callback /*, initialValue*/ ) {
    'use strict';
    if ( null === this || 'undefined' === typeof this ) {
      throw new TypeError(
         'Array.prototype.reduce called on null or undefined' );
    }
    if ( 'function' !== typeof callback ) {
      throw new TypeError( callback + ' is not a function' );
    }
    var t = Object( this ), len = t.length >>> 0, k = 0, value;
    if ( arguments.length >= 2 ) {
      value = arguments[1];
    } else {
      while ( k < len && ! k in t ) k++; 
      if ( k >= len )
        throw new TypeError('Reduce of empty array with no initial value');
      value = t[ k++ ];
    }
    for ( ; k < len ; k++ ) {
      if ( k in t ) {
         value = callback( value, t[k], k, t );
      }
    }
    return value;
  };
}

所有未经测试但我最近回答了一个类似的问题 here ,其中包含指向小提琴的链接。

答案 1 :(得分:0)

事实证明,在 jQuery-1.8 之前,使用一个参数调用$.then()相当于调用$.then(successFunction, successFunction)。由于我使用 jQuery-1.7.1 ,被拒绝的承诺仍会调用递归。