jQuery递归AJAX调用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 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 } 

这是JSFiddle http://jsfiddle.net/petrabarus/BHswy/

我知道如何在单个AJAX调用中使用promise,但我不知道如何在递归的AJAX调用中使用它。 我想以类似下面的方式调用requestAll函数

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

我怎样才能做到这一点?

如果要使用promises,则不应使用success参数。 相反,你想要return一个promise,然后你想使用then将promise的结果转换成不同的东西,甚至是另一个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); }); 

由于您已经一个接一个地对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 }); 

好的,经过很多新的承诺学习,这里是一个完全承诺的版本,它使用了promise chaining(从.then()处理程序返回一个promise)。 从Benji的实现中借鉴和学习的概念,但是它的组织方式略有不同,并且对于学习进行了评论(如果没有注释,没有虚拟的Ajax调用,它实际上会很短):

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

使用jsFiddle: http : //jsfiddle.net/jfriend00/pr5z9/ (请耐心等待,执行10次Ajax调用需要10秒钟,每次调用需要1秒钟)。

我注意到这个版本的一个问题是因为它只使用$.ajax()创建的promise,代码不能执行.notify()来触发进度通知。 我发现我想在每个Ajax调用完成后触发最初返回的promise的进度通知,但是没有创建我自己的Deferred,我不能这样做,因为你不能对.notify()做一个.notify()在延期。 我不确定如何解决这个问题,并坚持Benji的架构,即不创建/解决你自己的延迟。