【问题标题】:How do I pause resolution of a jQuery deferred in the middle of a chain?如何暂停在链中间延迟的 jQuery 的解析?
【发布时间】:2013-01-16 09:33:49
【问题描述】:

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

考虑 4 个按钮的序列。单击时,每个按钮都会禁用自身并启用下一个按钮。每个按钮在启用之前不应设置其事件。只有在启用第三个按钮后,才能完成添加的任务(在本例中为警报)。

基本 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() 函数将函数链接到 deferred 中。显然,这不起作用,因为第 3 步中的附加任务会在页面加载时触发。我需要如何调整此场景的控制或逻辑以解决每个步骤,直到注册了相应的按钮按下?

【问题讨论】:

  • 如果一个按钮被禁用,click事件永远不会触发,那么为什么不把所有的事件监听器绑定一次呢?
  • 因为这特别代表了如何处理延迟对象的问题。我试图拼凑如何将它们用于更复杂的问题,所以我试图与一个我能真正理解的更简单的问题进行对比。
  • 您在这里没有使用任何特定的延迟功能。你只是触发了四个连续的功能。您在此处尝试完成的操作与延迟无关。看我的回答。
  • 延迟对象文档建议它应该用于跨异步事件流水线操作。我正在设置这些函数来表示需要流水线的操作。在前一个动作实际完成(单击按钮)之前,不应执行每个动作。我不清楚如何在当前采取的行动实际完成时发出信号。如果我应该使用延迟对象以外的功能,那很好,但解决方案代表了我遇到的一般问题,而不是需要在我的网页上修复的特定问题。

标签: javascript jquery jquery-deferred promise


【解决方案1】:

您的代码示例并没有按照您的想法执行。您所做的只是在oDeferredObj 解决后直接添加到要完成的事情列表中。此外,还有一个问题是解决任务何时真正“完成”,您的代码并没有完全标记。

这似乎是我前段时间在另一个问题中解决过的问题,但我不确定我是否喜欢我当时给出的答案,所以让我重新尝试。

您正在寻找一种将承诺链接在一起的方法。我想,您还想要一种方式来说明承诺何时解决(或拒绝)。

从一个 Promise 到另一个 Promise 异步获取数据的一种好方法是使用 pipe 链接它们,但是由于您想从 UI 事件中触发任务的完成,我无法想象比我在下面有什么。

我不会发誓这是最好的方法,我能想到的最简单的处理方法是创建一个实用函数,它接受给定的承诺和“任务”,创建一个新的承诺,让您的任务决定如何处理承诺,但只有在给定的承诺解决后,然后返回新的承诺。

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();
  });
};

请注意,我只是在承诺成功时给了你一个开始。当他们失败时,做更多的事情是明智的。此外,如果您想将数据从一个 Promise 传递到另一个 Promise,您可能希望需要 pipe 等。

jsFiddle here中的完整源代码(根据您的修改)

【讨论】:

  • 这正是我试图解决的困惑。我想我有点被绊倒了,因为我仍然不习惯使用正确的词汇来解决javascript问题。非常感谢您。
【解决方案2】:

我不明白你为什么需要 deferred,但我知道你没有使用它们。

试试这样的。

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();

【讨论】:

  • 这些步骤与按钮点击有什么关系?我正在尝试集中控制页面以依赖于用户通过按钮单击的操作。从您的代码中看不出操作流程将如何围绕这些操作形成。
【解决方案3】:

也许您想使用队列,它是为一系列可能耗时的事件而设计的。它们与支持$.animate 的系统相同;一次只执行一个函数,直到第一个明确表示它以$.dequeue 结束时才会执行下一个函数。

另一方面,延迟实际上是在数据可用时使用数据。

queuedequeue。另请参阅我写的这个other recent answer,它有示例代码和一个jsfiddle。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-05-12
    • 2023-03-14
    • 1970-01-01
    • 2017-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多