错误在于您正在传递一个返回承诺的函数setTimeout
。这个承诺已经消失在空中。一个创可贴修复可能是在执行器函数上递归:
var checkIsFinished = function (athlete) {
return new Promise(function executor(resolve) {
if (athlete.distanceTravelled >= 100) {
clearInterval(intervalID);
console.log("finished");
resolve(athlete);
} else {
console.log("not finished yet, check again in a bit");
setTimeout(executor.bind(null, resolve), 1000);
}
});
};
但是嗯。我认为这是一个很好的例子,说明为什么人们应该避免承诺构造函数反模式 https://stackoverflow.com/q/23803743/1048572(因为混合承诺代码和非承诺代码不可避免地会导致这样的错误)。
我遵循的避免此类错误的最佳实践:
- 只处理返回 Promise 的异步函数。
- 当不返回 Promise 时,用 Promise 构造函数包装它。
- 尽可能窄地包装它(使用尽可能少的代码)。
- 不要将 Promise 构造函数用于其他用途。
在此之后,我发现代码更容易推理,更难出错,因为一切都遵循相同的模式。
将其应用到您的示例中(为了简洁起见,我使用 es6 箭头函数。它们在 Firefox 和 Chrome 45 中工作):
var console = { log: msg => div.innerHTML += msg + "<br>",
error: e => console.log(e +", "+ e.lineNumber) };
var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var startRunning = () => {
var athlete = {
timeTaken: 0,
distanceTravelled: 0,
intervalID: setInterval(() => {
athlete.distanceTravelled += 25;
athlete.timeTaken += 2.5;
console.log("updated athlete ");
}, 2500)
};
return wait(2000).then(() => athlete);
};
var checkIsFinished = athlete => {
if (athlete.distanceTravelled < 100) {
console.log("not finished yet, check again in a bit");
return wait(1000).then(() => checkIsFinished(athlete));
}
clearInterval(athlete.intervalID);
console.log("finished");
return athlete;
};
startRunning()
.then(checkIsFinished)
.then(athlete => console.log('printing time: ' + athlete.timeTaken))
.catch(console.error);
<div id="div"></div>
注意checkIsFinished
返回运动员或承诺。这在这里很好,因为.then
函数会自动提升传递给 Promise 的函数的返回值。如果你会打电话checkIsFinished
在其他情况下,您可能想自己进行促销,使用return Promise.resolve(athlete);
代替return athlete;
.
编辑回应阿米特的评论:
对于非递归答案,替换整个checkIsFinished
使用这个助手的功能:
var waitUntil = (func, ms) => new Promise((resolve, reject) => {
var interval = setInterval(() => {
try { func() && resolve(clearInterval(interval)); } catch (e) { reject(e); }
}, ms);
});
然后这样做:
var athlete;
startRunning()
.then(result => (athlete = result))
.then(() => waitUntil(() => athlete.distanceTravelled >= 100, 1000))
.then(() => {
console.log('finished. printing time: ' + athlete.timeTaken);
clearInterval(athlete.intervalID);
})
.catch(console.error);
var console = { log: msg => div.innerHTML += msg + "<br>",
error: e => console.log(e +", "+ e.lineNumber) };
var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var waitUntil = (func, ms) => new Promise((resolve, reject) => {
var interval = setInterval(() => {
try { func() && resolve(clearInterval(interval)); } catch (e) { reject(e); }
}, ms);
});
var startRunning = () => {
var athlete = {
timeTaken: 0,
distanceTravelled: 0,
intervalID: setInterval(() => {
athlete.distanceTravelled += 25;
athlete.timeTaken += 2.5;
console.log("updated athlete ");
}, 2500)
};
return wait(2000).then(() => athlete);
};
var athlete;
startRunning()
.then(result => (athlete = result))
.then(() => waitUntil(() => athlete.distanceTravelled >= 100, 1000))
.then(() => {
console.log('finished. printing time: ' + athlete.timeTaken);
clearInterval(athlete.intervalID);
})
.catch(console.error);
<div id="div"></div>