我正在尝试将序列化逻辑强制到网页上的一组异步活动中。我很确定我想使用jQuery延迟对象,但我遇到的问题是我想要执行的函数取决于用户何时决定通过单击各种按钮进行选择。我正在使用以下jsFiddle想法寻求帮助:
考虑一系列4个按钮。单击时,每个按钮都会自行禁用并启用下一个按钮。在启用之前,每个按钮都不应设置其事件。只有在启用第3个按钮后才能执行添加的任务(在这种情况下是警报)。
<button id="btn1">Click 1st</button>
<button id="btn2" disabled="disabled">Click 2nd</button>
<button id="btn3" disabled="disabled">Click 3rd</button>
<button id="btn4" disabled="disabled">Click 4th</button>
var fnDoStageOne = function() {
$("#btn1").one("click", function(event, ui) {
$("#btn1").prop("disabled", true);
$("#btn2").prop("disabled", false);
//STAGE ONE IS ONLY DONE AFTER THIS POINT
});
};
var fnDoStageTwo = function() {
$("#btn2").one("click", function(event, ui) {
$("#btn2").prop("disabled", true);
$("#btn3").prop("disabled", false);
//STAGE TWO IS ONLY DONE AFTER THIS POINT
});
};
var fnDoStageThree = function() {
$("#btn3").one("click", function(event, ui) {
$("#btn3").prop("disabled", true);
$("#btn4").prop("disabled", false);
//STAGE THREE IS ONLY DONE AFTER THIS POINT
});
alert("Shouldn't see this if button 3 isn't active yet");
};
var fnDoStageFour = function() {
$("#btn4").one("click", function(event, ui) {
$("#btn4").prop("disabled", true);
alert("Task complete");
//STAGE FOUR IS ONLY DONE AFTER THIS POINT
});
};
var oDeferredObj = $.Deferred();
oDeferredObj.then(fnDoStageOne);
oDeferredObj.then(fnDoStageTwo);
oDeferredObj.then(fnDoStageThree);
oDeferredObj.then(fnDoStageFour);
oDeferredObj.resolve();
这里可以看到jsfiddle:http://jsfiddle.net/sva79/
我最初的理解是我可以使用.then()函数将函数链接到延迟。显然,这不起作用,因为步骤3中的附加任务会在页面加载时触发。如何调整此方案的控制或逻辑以解决每个步骤,直到注册了相应的按钮?
答案 0 :(得分:3)
您的代码示例没有按照您的想法执行。您所做的就是在oDeferredObj
解决后直接添加要完成的事项列表。此外,还有一个问题就是在任务实际“完成”时解决,而你的代码并没有完全贴上标签。
这似乎是我之前在另一个问题上提到过的问题,但我不确定我是否愿意接受我给出的答案,所以让我重新尝试一下。
您正在寻求的是将新承诺链接在一起的方法。我认为,你还想要一种方式来说明承诺何时被解决(或被拒绝)。
将数据从一个承诺异步获取到另一个承诺的好方法是将它们与pipe
链接起来,但是当您想要在UI事件中触发任务的完成时,我很难想象某些东西比我下面的要好。
我不会发誓这是最好的方法,我能想到的最简单的方法就是创建一个实用功能,它接受一个给定的承诺和一个“任务”,创造一个新的承诺,让你的任务决定如何处理promise,但只有在解决了给定的promise之后,并返回新的promise。
var nextStage = function (promise, task) {
var oDeferredObj = $.Deferred();
promise.then(function () {
task(oDeferredObj);
});
return oDeferredObj;
}
这可用于将您的任务连接在一起:
/* Creation of deferred and initial wiring */
var p1;
var starterObj = $.Deferred();
p1 = starterObj;
p1 = nextStage(p1, fnDoStageOne);
p1 = nextStage(p1, fnDoStageTwo);
p1 = nextStage(p1, fnDoStageThree);
p1 = nextStage(p1, fnDoStageFour);
p1.done(function () {
alert("All stages done.");
})
$("#start").one("click", function (event, ui) {
//fire it up.
$("#start").prop("disabled", true);
});
显然,你会想要做某事来表明特定任务在某个时刻完成,例如:
var fnDoStageOne = function (promise) {
//setup, etc.
$("#btn1").one("click", function (event, ui) {
//.. whatever needs to happen in the ui.
//STAGE ONE IS ONLY DONE AFTER THIS POINT
promise.resolve();
});
};
请注意,我只是在承诺成功时给你一个开始。当他们失败时,做更多的事情是明智的。此外,如果您想将数据从一个承诺传递到另一个承诺,您可能需要pipe
等。
jsFiddle here
中的完整来源(由您自己修改)答案 1 :(得分:0)
我不明白为什么你需要推迟,但我知道你没有使用它们。
尝试这样的事情。
firstResolution = $.Deferred();
secondResolution = $.Deferred();
function stepOne() {
//disable button one
//enable button two
return firstResolution;
}
function stepTwo() {
//disable button two
//enable button three
}
$.when(stepOne()).then(stepTwo);
$.when(secondResolution).then(stepThree);
// This is also acceptable (and maybe better?)
// $.when(firstResolution, seconResolution).then(stepThree);
// when you resolve, then step two will fire.
firstResolution.resolve();
// and for stepThree to fire
secondResolution.resolve();
答案 2 :(得分:0)
也许您想要使用队列,这些队列是为可能耗时的事件序列而设计的。它们与$.animate
的力量相同;只有一个函数一次执行,而下一个函数在第一个明确表示已完成$.dequeue
之后才会执行。
请参阅queue和dequeue。另请参阅我写的other recent answer,其中包含示例代码和jsfiddle。