Следующее решение решает несколько конкретных проблем:
- как разорвать (иначе бесконечный) цикл
- как получить доступ к результату ранее неудачной попытки
- как включить
params = modify(params)
Проблема может быть разбита на две части:
- повторитель/промис-бегун (который сам возвращает промис, поэтому мы можем подключить к нему
onSuccess
)
- поставщик обещаний - функция, которая создает обещания для повторителя
Повторитель будет выглядеть так:
function repeatUntilSuccess(promiseProvider) {
return new Promise(function (resolve, reject) {
var counter = 0;
function run(failedResult) {
var p = promiseProvider(failedResult, counter++);
if (p instanceof Promise) {
p.then(resolve).catch(run);
} else {
reject(p);
}
}
run();
});
}
В этой строке происходит «петля»: p.then(resolve).catch(run);
. Повторитель продолжает вызывать поставщика обещаний до тех пор, пока возвращаемое им обещание не будет разрешено (в этом случае повторитель разрешается) или до тех пор, пока он больше не предоставляет обещание (в этом случае повторитель отклоняет).
Поставщик обещаний может быть любым function(previousResult, counter)
, который возвращает обещание (или нет, если вы хотите остановить цикл).
var params = {/* ... */};
function nextRequest(previousResult, counter) {
if (counter >= 10) return "too many attempts";
if (previousResult) params = modify(params);
return apiRequest(params);
}
(Эта установка предполагает, что apiRequest()
возвращает обещание)
Теперь вы можете сделать это:
repeatUntilSuccess(nextRequest).then(onSuccess).catch(onError);
Поскольку ваш вопрос включает в себя побочную задачу по заключению HTTP-запроса в обещание:
function apiRequest(params) {
return new Promise(function (resolve, reject) {
return request(params).then(function (result) {
if (isGood(result)) {
resolve(result);
} else {
reject(result);
}
});
});
}
Откройте консоль браузера и запустите следующий фрагмент кода, чтобы увидеть его в действии.
function repeatUntilSuccess(promiseProvider) {
return new Promise(function (resolve, reject) {
var counter = 0;
function run(failedResult) {
var p = promiseProvider(failedResult, counter++);
if (p instanceof Promise) {
p.then(resolve).catch(run);
} else {
reject(p);
}
}
run();
});
}
// mockup promise that resoves or rejects randomly after a timeout
function randomPromise(num){
return new Promise(function(resolve, reject){
setTimeout(function () {
var randomNum = Math.floor(Math.random() * num * 10);
if (randomNum < num) {
resolve(randomNum);
} else {
reject(randomNum);
}
}, 300);
});
}
// promise provider
function nextPromise(prev, i) {
if (prev) console.info("failed attempt #" + i + ": " + prev);
if (i >= 5) return "too many attempts:" + i;
return randomPromise(100);
}
// run it!
repeatUntilSuccess(nextPromise).then(function (result) {
console.log("success", result);
}).catch(function (result) {
console.log("failed", result);
});
person
Tomalak
schedule
18.02.2015