use*_*596 5 javascript recursion asynchronous promise es6-promise
我正在玩承诺,我遇到异步递归承诺的问题.
场景是运动员开始跑100米,我需要定期检查他们是否已经完成,一旦完成,打印他们的时间.
编辑澄清:
在现实世界中,运动员在服务器上运行.startRunning涉及对服务器进行ajax调用.checkIsFinished还涉及对服务器进行ajax调用.下面的代码试图模仿它.代码中的时间和距离是硬编码的,以尽量使事情变得简单.道歉不清楚.
结束编辑
我希望能够写下以下内容
startRunning()
.then(checkIsFinished)
.then(printTime)
.catch(handleError)
Run Code Online (Sandbox Code Playgroud)
哪里
var intervalID;
var startRunning = function () {
var athlete = {
timeTaken: 0,
distanceTravelled: 0
};
var updateAthlete = function () {
athlete.distanceTravelled += 25;
athlete.timeTaken += 2.5;
console.log("updated athlete", athlete)
}
intervalID = setInterval(updateAthlete, 2500);
return new Promise(function (resolve, reject) {
setTimeout(resolve.bind(null, athlete), 2000);
})
};
var checkIsFinished = function (athlete) {
return new Promise(function (resolve, reject) {
if (athlete.distanceTravelled >= 100) {
clearInterval(intervalID);
console.log("finished");
resolve(athlete);
} else {
console.log("not finished yet, check again in a bit");
setTimeout(checkIsFinished.bind(null, athlete), 1000);
}
});
};
var printTime = function (athlete) {
console.log('printing time', athlete.timeTaken);
};
var handleError = function (e) { console.log(e); };
Run Code Online (Sandbox Code Playgroud)
我可以看到,第一次创建的承诺checkIsFinished永远不会得到解决.如何确保该承诺得到解决以便printTime调用?
代替
resolve(athlete);
Run Code Online (Sandbox Code Playgroud)
我可以
Promise.resolve(athlete).then(printTime);
Run Code Online (Sandbox Code Playgroud)
但我想尽可能避免这种情况,我真的很想能够写作
startRunning()
.then(checkIsFinished)
.then(printTime)
.catch(handleError)
Run Code Online (Sandbox Code Playgroud)
错误是你传递的函数返回一个promise 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);
}
});
};
Run Code Online (Sandbox Code Playgroud)
但是,好吧.我认为这是一个很好的例子,说明为什么人们应该避免promise-constructor反模式(因为混合承诺代码和非承诺代码不可避免地导致这样的错误).
在此之后,我发现代码更易于推理并且更难以进行操作,因为所有内容都遵循相同的模式.
将此应用于您的示例让我在这里(为了简洁,我使用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);Run Code Online (Sandbox Code Playgroud)
<div id="div"></div>Run Code Online (Sandbox Code Playgroud)
请注意,checkIsFinished返回运动员或承诺.这很好,因为.then函数会自动提升您传递给promises的函数的返回值.如果您要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);
});
Run Code Online (Sandbox Code Playgroud)
然后这样做:
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);
Run Code Online (Sandbox Code Playgroud)
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);Run Code Online (Sandbox Code Playgroud)
<div id="div"></div>Run Code Online (Sandbox Code Playgroud)