您可以将一个新的Promise链接到前一个Promise上,从而延迟其最终解决方案,直到您知道最终答案。如果仍然不知道下一个答案,则在其上链接另一个Promise并继续将 checkStatus() 链接到自身,直到最终您知道答案并可以返回最终解决方案。这可以像这样工作:
function delay(t) {
return new Promise(function(resolve) {
setTimeout(resolve, t);
});
}
function checkStatus() {
return work.requestStatus().then(function(result) {
switch(result.status) {
case "success":
return result; // resolve
case "failure":
throw result; // reject
case default:
case "inProgress": //check every second
return delay(1000).then(checkStatus);
}
});
}
work.create()
.then(work.publish) //remote work submission
.then(checkStatus)
.then(function(){console.log("work published"})
.catch(console.error);
请注意,我还避免在您的switch
声明周围创建Promise。由于您已经在.then()
处理程序中,因此仅返回一个值是解决方案,抛出异常是拒绝,而返回Promise是将新Promise链接到前一个Promise。这涵盖了您switch
声明的三个分支,而无需在其中创建新的Promise。为方便起见,我确实使用了一个delay()
基于 Promise的函数。
仅供参考,这假设work.requestStatus()
不需要任何参数。如果它确实需要一些特定的参数,您可以在函数调用时传递这些参数。
为循环等待完成的时间实现某种超时值也可能是一个好主意,这样就不会永远持续下去。您可以像这样添加超时功能:
function delay(t) {
return new Promise(function(resolve) {
setTimeout(resolve, t);
});
}
function checkStatus(timeout) {
var start = Date.now();
function check() {
var now = Date.now();
if (now - start > timeout) {
return Promise.reject(new Error("checkStatus() timeout"));
}
return work.requestStatus().then(function(result) {
switch(result.status) {
case "success":
return result; // resolve
case "failure":
throw result; // reject
case default:
case "inProgress": //check every second
return delay(1000).then(check);
}
});
}
return check;
}
work.create()
.then(work.publish) //remote work submission
.then(checkStatus(120 * 1000))
.then(function(){console.log("work published"})
.catch(console.error);
我不确定您正在寻找什么“设计模式”。由于您似乎反对外部声明的checkStatus()
函数,这里有一个内联版本:
work.create()
.then(work.publish) //remote work submission
.then(work.requestStatus)
.then(function() {
// retry until done
var timeout = 10 * 1000;
var start = Date.now();
function check() {
var now = Date.now();
if (now - start > timeout) {
return Promise.reject(new Error("checkStatus() timeout"));
}
return work.requestStatus().then(function(result) {
switch(result.status) {
case "success":
return result; // resolve
case "failure":
throw result; // reject
case default:
case "inProgress": //check every second
return delay(1000).then(check);
}
});
}
return check();
}).then(function(){console.log("work published"})
.catch(console.error);
可以在许多情况下使用的更可重用的重试方案将定义一些可重用的外部代码,但您似乎反对这样做,所以我没有制作那个版本。
这是另一种.retryUntil()
方法,它Promise.prototype
根据您的请求使用一种方法。如果你想调整它的实现细节,你应该能够修改这个通用方法:
// fn returns a promise that must be fulfilled with an object
// with a .status property that is "success" if done. Any
// other value for that status means to continue retrying
// Rejecting the returned promise means to abort processing
// and propagate the rejection
// delay is the number of ms to delay before trying again
// no delay before the first call to the callback
// tries is the max number of times to call the callback before rejecting
Promise.prototype.retryUntil = function(fn, delay, tries) {
var numTries = 0;
function check() {
if (numTries >= tries) {
throw new Error("retryUntil exceeded max tries");
}
++numTries;
return fn().then(function(result) {
if (result.status === "success") {
return result; // resolve
} else {
return Promise.delay(delay).then(check);
}
});
}
return this.then(check);
}
if (!Promise.delay) {
Promise.delay = function(t) {
return new Promise(function(resolve) {
setTimeout(resolve, t);
});
}
}
work.create()
.then(work.publish) //remote work submission
.retryUntil(function() {
return work.requestStatus().then(function(result) {
// make this promise reject for failure
if (result.status === "failure") {
throw result;
}
return result;
})
}, 2000, 10).then(function() {
console.log("work published");
}).catch(console.error);
我仍然无法真正说出您想要什么,或者所有这些方法都没有解决您的问题。由于您的方法似乎都是内联代码而不是使用可重用的帮助程序,因此这里是其中之一:
work.create()
.then(work.publish) //remote work submission
.then(function() {
var tries = 0, maxTries = 20;
function next() {
if (tries > maxTries) {
throw new Error("Too many retries in work.requestStatus");
}
++tries;
return work.requestStatus().then(function(result) {
switch(result.status) {
case "success":
return result;
case "failure":
// if it failed, make this promise reject
throw result;
default:
// for anything else, try again after short delay
// chain to the previous promise
return Promise.delay(2000).then(next);
}
});
}
return next();
}).then(function(){
console.log("work published")
}).catch(console.error);