Javascript jQuery递归AJAX调用承诺

Javascript jQuery递归AJAX调用承诺,javascript,jquery,promise,Javascript,Jquery,Promise,我仍在试图弄清楚如何在递归AJAX调用中使用jQuery延迟对象。我有这样的代码 function request(page, items){ //building the AJAX return value for JSFiddle dummy AJAX endpoint var ret = { totalPage: 10, currentPage: page, items: [] }; for (var

我仍在试图弄清楚如何在递归AJAX调用中使用jQuery延迟对象。我有这样的代码

function request(page, items){    

    //building the AJAX return value for JSFiddle dummy AJAX endpoint
    var ret = {
        totalPage: 10,
        currentPage: page,
        items: []
    };
    for (var i = page; i < (page + 5); i++){
        ret.items.push(i);
    }

    //calling the AJAX
    $.ajax({
        url: '/echo/json/',
        method: 'POST',
        dataType: 'json',
        data: {
            delay: 1,
            json: JSON.stringify(ret)
        },
        success: function(data){
            if (data.currentPage <= data.totalPage){
                var filtered = data.items.filter(function(el){
                    return el % 2 == 1;
                });
                var newitems = items.concat(filtered);
                console.dir(newitems);
                request(data.currentPage + 1, newitems);
            } else {
                console.dir(items);
                //resolve all item
            }
        }
    });
}

function requestAll(){
    request(1, []);
    //should return a promise tha contains all items
}

我如何才能做到这一点?

如果您希望使用承诺,则不应使用
success
参数。相反,您希望
返回
承诺,并希望使用
然后
将承诺的结果转换为不同的内容,甚至可能是另一个承诺

function request(page) {    
    …
    // return the AJAX promise
    return $.ajax({
        url: '/echo/json/',
        method: 'POST',
        dataType: 'json',
        data: {
            delay: 1,
            json: JSON.stringify(ret)
        }
    });
}

function requestOddsFrom(page, items) {
    return request(page).then(function(data){
        if (data.currentPage > data.totalPage) {
            return items;
        } else {
            var filtered = data.items.filter(function(el){ return el%2 == 1; });
            return requestOddsFrom(data.currentPage + 1, items.concat(filtered));
        }
    });
}

function requestAll(){
    return requestOddsFrom(1, []);
}

requestAll().then(function(items) {
    console.dir(items);
});

由于您已经在一个接一个地对Ajax操作进行排序,而无需完全重构代码,因此您可以只使用上次Ajax调用中解析的一个延迟:

function request(page, items, defer){    

    //building the AJAX return value for JSFiddle dummy AJAX endpoint
    var ret = {
        totalPage: 10,
        currentPage: page,
        items: []
    };
    for (var i = page; i < (page + 5); i++){
        ret.items.push(i);
    }

    //calling the AJAX
    $.ajax({
        url: '/echo/json/',
        method: 'POST',
        dataType: 'json',
        data: {
            delay: 1,
            json: JSON.stringify(ret)
        },
        success: function(data){
            if (data.currentPage <= data.totalPage){
                var filtered = data.items.filter(function(el){
                    return el % 2 == 1;
                });
                var newitems = items.concat(filtered);
                console.dir(newitems);
                request(data.currentPage + 1, newitems, defer);
            } else {
                console.dir(items);
                //resolve the deferred
                defer.resolve(items);
            }
        }
    });
}

function requestAll(){
    var deferred = jQuery.Deferred();
    request(1, [], deferred);
    return deferred.promise();
}

requestAll().done(function(items) {
    // all ajax calls are done
});
使用JSFIDLE:(耐心点,执行10个Ajax调用需要10秒,每个调用需要1秒)


我注意到这个版本的一个问题是,因为它只使用由
$.ajax()
创建的承诺,所以代码不能执行
.notify()
来触发进度通知。我发现我想在每次Ajax调用完成时触发对最初返回的承诺的进度通知,但如果不创建我自己的延迟,我就不能这样做,因为您不能对承诺执行
.notify()
,而只能对延迟的承诺执行。我不知道如何解决这一问题,也不知道如何保持本吉的体系结构,即不创建/解决您自己的延迟问题。

我认为,如果您这样做,您仍然可以使用一个承诺:啊,我不知道我是否能做到这一点。谢谢。@JasonP:这并没有真正使用承诺的力量……仅供参考,您的代码实际上不是递归的,因为
request()
在您从
成功
处理程序调用下一个函数之前已经完成了执行。我想我仍然可以将其称为递归,因为AJAX实际上会调用与之前调用它相同的函数。为什么要进行下一票?这正是OP要求的。它还对OP的现有代码进行了最少的更改。它的可恢复性较差,不能正确使用承诺,并且比Bergi的解决方案更长。此外,递归在一个更长的函数上。承诺已经有了现成的continuation,这种没有返回值的回调递归在这里是不必要的。此外,如果使用真正的promise库,它将提供更糟糕的堆栈跟踪。参见@BenjaminGruenbaum-它如何不正确地使用承诺?Bergi的解决方案方便地省略了OP所需的一组代码(替换为…),所以我不知道长度。还有,为什么要否决一个正确的解决方案呢。再投一个你喜欢的。如果需要,您可以拥有自己喜欢的体系结构,但这样可以避免重写整个OP的代码,并且它完全按照OP的要求执行。通常不值得投否决票。这里也没有实际的递归,所以我不知道你在说什么堆栈跟踪。下行投票不是为了正确性,而是为了有用性——这是我投票的方式,也是我投票的唯一方式。考虑到Bergi现有的解决方案,我发现您的解决方案没有什么用处,它鼓励promise用户社区试图消除的反模式(例如,您的代码不是抛出安全的(大问题),它提供的调试信息较少,除了返回值之外不使用Promission,而且不太清楚)。很抱歉,你不欣赏我的投票,但我认为你会做到这一点-2考虑到我在过去多次投票支持你的有用答案。我更喜欢Bergi的代码。它看起来更干净,更模块化,因为我可以很容易地修改过滤功能。而将一个承诺的结果转化为另一个承诺的想法正是我所想的。我的问题有点假,因为当时我不清楚我想问什么。被删除的代码实际上只是JSFIDLE的一个虚拟AJAX端点(因此可以删除它)。实际上它非常简单,OP已经有了尾部递归,甚至比我最初想象的还要简单。这里的问题是,
requestOddsFrom(…)
的“递归”调用将被
return
所采用,然后将其返回到
then
,从而使整体结果成为内部承诺的结果。只有当从
.then()
处理程序返回承诺时,理解承诺如何链接的人才能明白这一点。这无论如何都不是一个简单的概念(许多人在不了解使用级别的情况下使用承诺),也不容易理解代码逻辑如何工作或承诺如何流动。也许一旦你完全摸索它,它是微不足道的,但是请对那些没有你那么先进的人有一些同理心。你对它的解释非常直截了当,但这并不能帮助我理解它是如何工作的,这就是我希望你能进一步解释的
requestOddsFrom()
通过调用
request()返回值。然后()
。这将是
.then()
返回的第二代承诺。我能做到这一点。我不明白的是,对
requestOddsFrom()
的N次调用如何将数据返回到
requestAll()中该承诺的
.done()
处理程序。我试着查看jQuery
.then()
代码,看看我是否能理解它,但这是我见过的最迟钝的代码之一(我想是为了让它变小),我无法理解它的逻辑。jQuery文档在这个话题上也没有帮助。@jfriend00:也许你不明白“第二代承诺”是什么意思。它是一个承诺,将通过从
然后
回调返回的
requestOddsFrom(…)
递归调用承诺的结果来解决。或者,它将只解决从
返回的
项,然后在没有更多页面的情况下进行
回调。是的,我不明白对
requestOddsFrom()的N个递归调用如何将数据返回到从原始调用
requestOddsFrom()返回的第一个承诺
function request(page, items, defer){    

    //building the AJAX return value for JSFiddle dummy AJAX endpoint
    var ret = {
        totalPage: 10,
        currentPage: page,
        items: []
    };
    for (var i = page; i < (page + 5); i++){
        ret.items.push(i);
    }

    //calling the AJAX
    $.ajax({
        url: '/echo/json/',
        method: 'POST',
        dataType: 'json',
        data: {
            delay: 1,
            json: JSON.stringify(ret)
        },
        success: function(data){
            if (data.currentPage <= data.totalPage){
                var filtered = data.items.filter(function(el){
                    return el % 2 == 1;
                });
                var newitems = items.concat(filtered);
                console.dir(newitems);
                request(data.currentPage + 1, newitems, defer);
            } else {
                console.dir(items);
                //resolve the deferred
                defer.resolve(items);
            }
        }
    });
}

function requestAll(){
    var deferred = jQuery.Deferred();
    request(1, [], deferred);
    return deferred.promise();
}

requestAll().done(function(items) {
    // all ajax calls are done
});
function requestPages(startPage, endPage) {

    function request(page, items){    
        // building the AJAX return value for 
        // JSFiddle dummy AJAX endpoint
        var ret = {
            currentPage: page,
            items: []
        };
        for (var i = page; i < (page + 5); i++){
            ret.items.push(i);
        }

        // Do Ajax call, return its promise
        return $.ajax({
            url: '/echo/json/',
            method: 'POST',
            dataType: 'json',
            data: {
                delay: 1,
                json: JSON.stringify(ret)
            }
        }).then(function(data) {
            // mock filter here to give us just odd values
            var filtered = data.items.filter(function(el){
                return el % 2 == 1;
            });
            // add these items to the ones we have so far
            items = items.concat(filtered);

            // if we have more pages to go, then do the next one
            if (page < endPage){
                // Advance the currentPage, call function to process it and
                // return a new promise that will be chained back to the 
                // promise that was originally returned by requestPages()
                return request(page + 1, items);
            } else {
                // Finish our iteration and 
                // return the accumulated items.
                // This will propagate back through 
                // all the other promises to the original promise
                // that requestPages() returned
                return(items);
            }
        });
    }    

    // call the first request and return it's promise    
    return request(startPage, []);
}

// request pages 1 through 10 inclusive
requestPages(1, 10).done(function(items) {
    // all ajax calls are done
    console.log(items);
});