我有一个具有以下基本设置的项目...(我将其剥离到必要的部分,但引擎对象中除了setHtml之外还有其他功能)
var engine = {
setHtml:function(){
htmlGenerator(callback)
//if htmlGenerator takes 5 or more seconds to respond, skip the item.
//Otherwise, continue with regular flow
function skip(){
//skip the current item
}
function callback(){
//continue with regular flow
}
}
}
看起来很简单,但有一些挑战......
htmlGenerator位于另一个文件中。我无法知道该功能是同步还是异步,理想情况并不重要。
htmlGenerator会在准备就绪时调用callback()。
应该调用skip()或callback()中的一个,而不是两个或都没有。
如果htmlGenerator花费超过5秒来调用回调,我想调用skip()。这并不意味着htmlGenerator不会调用callback()。
skip()可以递归调用,但不能确定它是否会被递归调用。 (即跳过调用的一部分是使用一组新数据再次调用setHtml,这可能会或可能不会导致另一个调用花费5秒或更长时间)
这是一个前端应用程序,所以我希望尽可能避免引入额外的库/模块/包,因为加载时间是首要任务。
有一段时间,我有类似下面的内容,但由于我无法解释的原因,这些内容并不起作用。
var engine = {
skipTimer:null
setHtml:function(){
htmlGenerator(callback)
//if htmlGenerator takes 5 or more seconds to respond, skip the item.
//Otherwise, continue with regular flow
engine.skipTimer = setTimeout(skip,5000)
function skip(){
console.log(engine.skipTimer)//this would correctly display the timer's ID.
//skip the current item
}
function callback(){
console.log(engine.skipTimer)//this was "false" or "null" or something to that effect
clearTimeout(engine.skipTimer) //for some reason, the timer was never cleared
//continue with regular flow
}
}
}
在我的测试用例中,我没有用多次失败(跳过)调用该函数,但是我确实需要它来处理它。
我也试过以下,但收效甚微......
var engine = {
setHtml:function(){
var called = false;
htmlGenerator(callback)
//if htmlGenerator takes 5 or more seconds to respond, skip the item.
//Otherwise, continue with regular flow
engine.skipTimer = setTimeout(skip,5000)
function skip(){
if (called)return;
called = true;
//skip the current item
}
function callback(){
if(called)return;
called = true;
//continue with regular flow
}
}
}
如果你理解为什么这些结果失败了,或者如何解决这个问题,我们将非常感谢任何帮助。
答案 0 :(得分:3)
由于htmlGenerator可能是异步或同步的,因此使用Promise
是个好主意。具体来说是Promise.race()
。
var engine = {
setHtml:function(){
var called = false;
var p1 = new Promise(function(resolve){
htmlGenerator(function(){
resolve(true)
});
}
//if htmlGenerator takes 5 or more seconds to respond, skip the item.
//Otherwise, continue with regular flow
var p2 = new Promise(function(resolve) {
setTimeout(function(){
resolve(false)
}, 5000);
});
return Promise.race([p1,p2]);
}
}
以上意味着你的engine.setHtml将返回一个Promise,如果htmlGenerator首先调用它,则返回true
,如果5秒超时,则返回false
。
所以用法有点像这样(我假设
)engine.setHtml().then(function(generated){
if (generated){
// Do stuff here for when htmlGenerator "wins" the callback race.
} else {
// Generator lost, you want to skip, do whatever things you need to do to "skip".
}
});
请注意,如果您想继续使用setHtml函数而不是return Promise.race([p1,p2])
,请执行此操作。
Promise.race([p1,p2]).then(function(generated){
if (generated){
// Do stuff here for when htmlGenerator "wins" the callback race.
} else {
// Generator lost, you want to skip, do whatever things you need to do to "skip".
}
});
一点点解释。第一个代码块基本上创建了两个" Promises"可以单独解决。当htmlGenerator调用其回调时,Promise p1
将解析为true(resolve(true)
)。当setTimeout超时(在您的情况下为5秒)时,Promise p2
将解析为false(resolve(false)
)。
Promise.race将解决使用哪个Promise首先解决的问题。因此,如果htmlGenerator首先调用其回调,则Promise.race
会解析为p1
的值,并将其解析为true
。相反,如果setTimeout首先调用其回调,Promise.race
将解析为p2
的值,并将其解析为false
。最后,当您使用Promise.race([p1,p2]).then(callback)
时,回调会收到已解决的Promise.race([p1,p2])
值,这将是赢得比赛的承诺[p1,p2]的已解决值。
答案 1 :(得分:1)
同步处理htmlGenerator()
时会出现您描述的问题。在这种情况下,htmlGenerator
以及callback()
将在调用setTimeout
之前执行,因此您在{{1}上调用clearTimeout
}}。这意味着没有清除计时器。然后,只要null
完成,就会启动计时器,因此始终会调用htmlGenerator
。
要解决此问题,您只需在调用skip
之前设置计时器:
htmlGenerator
要清楚地查看差异,请检查this working fiddle中的控制台,该控制台仅显示var engine = {
skipTimer:null
setHtml:function() {
engine.skipTimer = setTimeout(skip, 5000);
htmlGenerator(callback);
function skip() {
console.log(engine.skipTimer)
}
function callback(){
console.log(engine.skipTimer);
clearTimeout(engine.skipTimer);
}
}
}
功能中的计时器ID,而your original version首先显示callback()
null
来自回调,然后来自skip()
的计时器ID。