我有一个api调用,有时会返回分页响应。我想自动将这些添加到我的承诺中,这样我就可以在所有数据到达后收到回调。
这是我的尝试。我希望可以添加新的承诺,并且一旦完成,Promise.all就会解决。
实际发生的是Promise.all不等待第二个请求。我的猜测是Promise.all在调用时会附加“听众”。
有没有办法“重新融合”Promise.all()?
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);
});
}
所需的流程类似于:
答案 0 :(得分:3)
看起来总体目标是:
urls
中的每个条目,请致电$.get
并等待其完成。
response
调用回调。我会更改#2,因此您只需返回承诺并使用response
解决此问题。
关于承诺的关键是then
会返回一个新承诺,该承诺将通过您从then
返回的内容得到解决(如果您返回非{ -thenable值,或间接的,如果你返回一个thenable,通过奴役自己的那个)。这意味着如果你有一个承诺来源(在这种情况下为$.get
),你几乎不需要使用new Promise
;只需使用您使用then
创建的承诺。 (和catch
。)
见评论:
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;
});
}
请注意我们从未在那里使用catch
;我们将错误处理推迟给调用者。
用法:
testCase(["url1", "url2", "etc."])
.then(function(responses) {
// Use `responses` here
})
.catch(function(error) {
// Handle error here
});
testCase
函数看起来很长,但这只是因为评论。这里没有他们:
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的箭头功能,它会更简洁。 : - )
在评论中你问过:
如果有下一个下一个可以处理吗?喜欢第3页的结果?
我们可以通过将该逻辑封装到我们使用的函数而不是$.get
中来实现,我们可以递归使用它:
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;
}
});
}
然后我们的主testCase
更简单:
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;
});
}
答案 1 :(得分:1)
假设您使用的是jQuery v3 +,您可以使用$.ajax
返回的承诺传递给Promise.all()
。
您缺少的是将第二个请求作为承诺返回,而不是尝试将其推送到promises数组
简化示例
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 强>