如何暂停在链中间推迟的jQuery的解析?

时间:2013-01-15 23:28:34

标签: javascript jquery jquery-deferred promise

我正在尝试将序列化逻辑强制到网页上的一组异步活动中。我很确定我想使用jQuery延迟对象,但我遇到的问题是我想要执行的函数取决于用户何时决定通过单击各种按钮进行选择。我正在使用以下jsFiddle想法寻求帮助:

考虑一系列4个按钮。单击时,每个按钮都会自行禁用并启用下一个按钮。在启用之前,每个按钮都不应设置其事件。只有在启用第3个按钮后才能执行添加的任务(在这种情况下是警报)。

基本HTML代码

<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中的附加任务会在页面加载时触发。如何调整此方案的控制或逻辑以解决每个步骤,直到注册了相应的按钮?

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之后才会执行。

另一方面,延迟实际上就是在数据可用后消耗数据。

请参阅queuedequeue。另请参阅我写的other recent answer,其中包含示例代码和jsfiddle。