看起来总体目标是:
- 对于
urls 中的每个条目,调用$.get 并等待它完成。
- 如果它只返回一个没有“下一个”的响应,则保留那个响应
- 如果它返回带有“下一个”的响应,我们希望也请求“下一个”,然后保留它们。
- 所有工作完成后,使用
response 调用回调。
我会更改 #2,因此您只需返回承诺并使用 response 履行它。
关于 Promise 的一个关键是 then 返回一个 new Promise,它将根据您返回的内容来解决:如果您返回一个非 thenable 值,则该 Promise 将通过该值实现价值;如果您返回一个 thenable,则承诺将解析为您返回的 thenable。这意味着如果你有一个 Promise 的来源(在这种情况下是$.get),你几乎不需要使用new Promise;只需使用您通过then 创建的承诺。 (还有catch。)
(如果“thenable”一词不熟悉,或者您不清楚“fulfill”和“resolve”之间的区别,我会在我的博客上的this post 中介绍promise 术语。)
见 cmets:
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 fulfill (via `return`)
// with an array with both the original response and the
// "next". Note that by returning a thenable, we resolve the
// promise created by `then` 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 函数看起来很长,但这仅仅是因为 cmets。这里没有它们:
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;
});
}