Похоже, что общая цель такова:
- For each entry in
urls
, call $.get
and wait for it to complete.
- If it returns just a response without "next", keep that one response
- Если он возвращает ответ со следующим, мы также хотим запросить следующий, а затем сохранить их оба.
- Вызовите обратный вызов с помощью
response
, когда вся работа будет выполнена.
Я бы изменил № 2, чтобы вы просто вернули обещание и выполнили его с помощью response
.
Ключевым моментом промисов является то, что then
возвращает новое обещание, которое будет разрешено на основе того, что вы вернете: если вы вернете значение, которое нельзя изменить, обещание будет выполнено с этим значением; если вы возвращаете thenable, обещание разрешается возвращаемому вами thenable. Это означает, что если у вас есть источник обещаний (в данном случае $.get
), вам почти никогда не понадобится использовать new Promise
; просто используйте обещания, которые вы создаете с помощью then
. (И catch
.)
(Если термин thenable вам не знаком или вы не понимаете разницу между выполнением и разрешением, я расскажу о терминологии обещаний в этот пост в моем блоге.)
Смотрите комментарии:
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
выглядит очень длинной, но это только из-за комментариев. Вот без них:
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;
});
}
person
T.J. Crowder
schedule
13.02.2017
promises
? Я вижу, как вы подталкиваете к этому, но я не вижу, как вы это создаете. - person T.J. Crowder   schedule 13.02.2017url
? (Если это массив, обычно это множественное число, например,urls
.) - person T.J. Crowder   schedule 13.02.2017response.resource = response.resource.concat(v.resource);
? Это создает новый массив каждый раз...? - person T.J. Crowder   schedule 13.02.2017response.meta.next
, хотите ли вы, чтобы и исходный ответ, и следующий ответ были в результате? - person T.J. Crowder   schedule 13.02.2017urls
, а не по результатуresponse.meta.next
. - person T.J. Crowder   schedule 13.02.2017Promises.all
. - person Josiah   schedule 13.02.2017