Adding a Promise to Promise.all()
我有一个api调用,有时会返回分页的响应。 我想自动将这些添加到我的诺言中,以便在所有数据到达后获取回调。
这是我的尝试。 我希望添加新的承诺,并在完成后解决Promise.all。
实际发生的是Promise.all不等待第二个请求。 我的猜测是Promise.all在调用时会附加"侦听器"。
有没有办法"重新初始化" Promise.all()?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | function testCase (urls, callback) { var promises = []; $.each(urls, function (k, v) { promises.push(new Promise(function(resolve, reject) { $.get(v, function(response) { if (response.meta && response.meta.next) { promises.push(new Promise(function (resolve, reject) { $.get(v + '&offset=' + response.meta.next, function (response) { resolve(response); }); })); } resolve(response); }).fail(function(e) {reject(e)}); })); }); Promise.all(promises).then(function (data) { var response = {resource: []}; $.each(data, function (i, v) { response.resource = response.resource.concat(v.resource); }); callback(response); }).catch(function (e) { console.log(e); }); } |
所需的流程类似于:
看起来总体目标是:
- 如果它只返回一个响应而不包含" next",则保留该响应
- 如果它返回带有" next"的响应,我们也要请求" next",然后将它们都保留。
我将更改#2,以便您只返回诺言并使用
关于promise的关键是
看评论:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | function testCase(urls) { // Return a promise that will be settled when the various `$.get` calls are // done. return Promise.all(urls.map(function(url) { // Return a promise for this `$.get`. return $.get(url) .then(function(response) { if (response.meta && response.meta.next) { // This `$.get` has a"next", so return a promise waiting // for the"next" which we ultimately resolve (via `return`) // with an array with both the original response and the //"next". Note that since we're returning a thenable, the // promise created by `then` will slave itself to the // thenable we return. return $.get(url +"&offset=" + response.meta.next) .then(function(nextResponse) { return [response, nextResponse]; }); } else { // This `$.get` didn't have a"next", so resolve this promise // directly (via `return`) with an array (to be consistent // with the above) with just the one response in it. Since // what we're returning isn't thenable, the promise `then` // returns is resolved with it. return [response]; } }); })).then(function(responses) { // `responses` is now an array of arrays, where some of those will be one // entry long, and others will be two (original response and next). // Flatten it, and return it, which will settle he overall promise with // the flattened array. var flat = []; responses.forEach(function(responseArray) { // Push all promises from `responseArray` into `flat`. flat.push.apply(flat, responseArray); }); return flat; }); } |
请注意,我们在那里永远不使用
用法:
1 2 3 4 5 6 7 | testCase(["url1","url2","etc."]) .then(function(responses) { // Use `responses` here }) .catch(function(error) { // Handle error here }); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | function testCase(urls) { return Promise.all(urls.map(function(url) { return $.get(url) .then(function(response) { if (response.meta && response.meta.next) { return $.get(url +"&offset=" + response.meta.next) .then(function(nextResponse) { return [response, nextResponse]; }); } else { return [response]; } }); })).then(function(responses) { var flat = []; responses.forEach(function(responseArray) { flat.push.apply(flat, responseArray); }); return flat; }); } |
...如果我们使用ES2015的arrow函数,它将更加简洁。 :-)
在评论中,您询问:
Could this handle if there was a next next? Like a page 3 of results?
我们可以通过将该逻辑封装到我们使用的函数中来代替
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function getToEnd(url, target, offset) { // If we don't have a target array to fill in yet, create it if (!target) { target = []; } return $.get(url + (offset ?"&offset=" + offset :"")) .then(function(response) { target.push(response); if (response.meta && response.meta.next) { // Keep going, recursively return getToEnd(url, target, response.meta.next); } else { // Done, return the target return target; } }); } |
然后我们的主要
1 2 3 4 5 6 7 8 9 10 11 | function testCase(urls) { return Promise.all(urls.map(function(url) { return getToEnd(url); })).then(function(responses) { var flat = []; responses.forEach(function(responseArray) { flat.push.apply(flat, responseArray); }); return flat; }); } |
假设您正在使用jQuery v3 +,则可以使用
您缺少的是将第二个请求作为promise返回,而不是尝试将其推送到promise数组
简化的例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | var promises = urls.map(function(url) { // return promise returned by `$.ajax` return $.get(url).then(function(response) { if (response.meta) { // return a new promise return $.get('special-data.json').then(function(innerResponse) { // return innerResponse to resolve promise chain return innerResponse; }); } else { // or resolve with first response return response; } }); }) Promise.all(promises).then(function(data) { console.dir(data) }).catch(function(e) { console.log(e); }); |
DEMO