操作が成功するか、一定の時間が経過するまで、操作ごとにタイムアウトを増やしながら操作を繰り返し実行したい。 Qで約束を使ってこれをどのように構成しますか?
ここでのすべての答えは私の意見では本当に複雑です。 Kosは正しい考えを持っていますが、より慣用的なpromiseコードを書くことでコードを短くすることができます。
function retry(operation, delay) {
return operation().catch(function(reason) {
return Q.delay(delay).then(retry.bind(null, operation, delay * 2));
});
}
そしてコメント付き:
function retry(operation, delay) {
return operation(). // run the operation
catch(function(reason) { // if it fails
return Q.delay(delay). // delay
// retry with more time
then(retry.bind(null, operation, delay * 2));
});
}
特定の時間(10秒としましょう)後にタイムアウトにしたい場合は、次のようにします。
var promise = retry(operation, 1000).timeout(10000);
その機能はQに組み込まれているため、再発明する必要はありません:)
これは、いくつかのヘルパー関数を使用して、これに私がどのようにアプローチするかの例です。 2つの状態を競合させる必要があるため、「maxTimeout」はより複雑な部分です。
// Helper delay function to wait a specific amount of time.
function delay(time){
return new Promise(function(resolve){
setTimeout(resolve, time);
});
}
// A function to just keep retrying forever.
function runFunctionWithRetries(func, initialTimeout, increment){
return func().catch(function(err){
return delay(initialTimeout).then(function(){
return runFunctionWithRetries(
func, initialTimeout + increment, increment);
});
});
}
// Helper to retry a function, with incrementing and a max timeout.
function runFunctionWithRetriesAndMaxTimeout(
func, initialTimeout, increment, maxTimeout){
var overallTimeout = delay(maxTimeout).then(function(){
// Reset the function so that it will succeed and no
// longer keep retrying.
func = function(){ return Promise.resolve() };
throw new Error('Function hit the maximum timeout');
});
// Keep trying to execute 'func' forever.
var operation = runFunctionWithRetries(function(){
return func();
}, initialTimeout, increment);
// Wait for either the retries to succeed, or the timeout to be hit.
return Promise.race([operation, overallTimeout]);
}
次に、これらのヘルパーを使用するには、次のようにします。
// Your function that creates a promise for your task.
function doSomething(){
return new Promise(...);
}
runFunctionWithRetriesAndMaxTimeout(function(){
return doSomething();
}, 1000 /* start at 1s per try */, 500 /* inc by .5s */, 30000 /* max 30s */);
私はあなたがそれをプロミスレベルで行うことはできないと思います-プロミスは操作ではなく、将来到着する単なる値なので、達成できるPromise -> Promise
と型指定された関数を定義することはできませんそれ。
1レベル下に移動して、Operation -> Promise
と入力された関数を定義する必要があります。ここで、Operation自体は() -> Promise
と入力されています。この操作はパラメーターをとらないと思います。事前に部分的に適用できます。
以下は、実行ごとにタイムアウトを2倍にする再帰的なアプローチです。
function RepeatUntilSuccess(operation, timeout) {
var deferred = Q.defer();
operation().then(function success(value) {
deferred.resolve(value);
}, function error(reason) {
Q.delay(timeout
.then(function() {
return RepeatUntilSuccess(operation, timeout*2);
}).done(function(value) {
deferred.resolve(value);
});
});
return deferred.promise;
}
このようなもの:
var goOn= true;
setTimeout(function () {
goOn= false;
}, 30000); // 30 seconds -- all process timeout
var timeout = 1000; // 1 second
(function () {
var helperFunction = function () {
callAsyncFunc().then(function () {
// success...
}, function () {
// fail
if (goOn) {
timeout += 1000; // increase timeout 1 second
helperFunction();
}
}).timeout(timeout);
}
})();
私はPromises/A +で次のことを行いました(Qでは問題ありません)
function delayAsync(timeMs)
{
return new Promise(function(resolve){
setTimeout(resolve, timeMs);
});
}
//use an IIFE so we can have a private scope
//to capture some state
(function(){
var a;
var interval = 1000;
a = function(){
return doSomethingAsync()
.then(function(success){
if(success)
{
return true;
}
return delayAsync(interval)
.then(function(){
interval *= 2;
})
.then(a());
});
};
a();
})();
最大タイムアウト後に保釈する方法を理解できると思います。