最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - jQuery Recursive AJAX Call Promise - Stack Overflow

programmeradmin4浏览0评论

I'm still trying to figure out how to use jQuery deferred object in recursive AJAX call. I have a code like this

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
}

Here's the JSFiddle /

I know how to use promise in single AJAX call, but I have no idea how to use it in a recursive AJAX call. I want to call the requestAll function in a way similar like below

var promise = requestAll();
promise.done(function(items){
    console.dir(items);
});

How can I do this?

I'm still trying to figure out how to use jQuery deferred object in recursive AJAX call. I have a code like this

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
}

Here's the JSFiddle http://jsfiddle.net/petrabarus/BHswy/

I know how to use promise in single AJAX call, but I have no idea how to use it in a recursive AJAX call. I want to call the requestAll function in a way similar like below

var promise = requestAll();
promise.done(function(items){
    console.dir(items);
});

How can I do this?

Share Improve this question edited Aug 30, 2015 at 18:59 royhowie 11.2k14 gold badges53 silver badges67 bronze badges asked Apr 29, 2014 at 3:56 Petra BarusPetra Barus 4,0138 gold badges52 silver badges92 bronze badges 7
  • 2 I think you could still use a single promise if you did something like this: jsfiddle.net/gGAAy – Jason P Commented Apr 29, 2014 at 4:04
  • ah, i didn't know if i can do that. thanks. – Petra Barus Commented Apr 29, 2014 at 4:14
  • 1 @JasonP: That's not really using the power of promises… – Bergi Commented Apr 29, 2014 at 4:27
  • FYI, your code isn't actually recursive because request() has already finished executing before you call the next one from the success handler. – jfriend00 Commented Apr 29, 2014 at 6:26
  • I think I still can call it as recursive because the AJAX will actually call the same function that called it before. – Petra Barus Commented Apr 29, 2014 at 8:08
 |  Show 2 more comments

2 Answers 2

Reset to default 18

You should not use the success parameter if you want to work with promises. Instead, you want to return a promise, and you want to use then to transform the results of a promise into something different, possibly even another promise.

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);
});

Since you're already sequencing the Ajax operations one after the other, without totally restructing your code, you can just use one deferred that you resolve on the last Ajax call:

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
});

OK, after much new promise learning, here's a fully promise version that makes use of promise chaining (returning a promise from a .then() handler). Concepts borrowed and learned from Benji's implementation, but this is organized a bit differently and commented for learning (it would actually be quite short without comments and without the dummy Ajax call stuff):

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);
});

Working jsFiddle: http://jsfiddle.net/jfriend00/pr5z9/ (be patient, it takes 10 seconds to execute for 10 Ajax calls that each take 1 second).

One issue I noticed about this version is that because it only uses the promises created by $.ajax(), the code cannot do a .notify() to trigger progress notifications. I found that I wanted to trigger a progress notification on the originally returned promise as each Ajax call completed, but without creating my own Deferred, I couldn't do that because you can't do a .notify() on a promise, only on a Deferred. I'm not sure how to solve that and keep with Benji's architecture of not creating/resolving your own deferred.

发布评论

评论列表(0)

  1. 暂无评论