【问题标题】:JQuery Deferred: wait for result synchronouslyJQuery Deferred:同步等待结果
【发布时间】:2016-11-25 13:09:10
【问题描述】:

我有一个在用户离开页面时调用的函数。此功能可确保始终保存页面上的未决更改。如果保存失败,可以通过此方法返回false 来取消导航。这是应用程序中的现有代码,无法更改以使用回调(旧版)。

此方法用于导航元素的 OnClick 处理程序(我知道,旧版):

<a href="link" onclick="return saveOnNavigation()">link</a>

现在我有了这个新函数,它使用 jQuery.Deferred 来保存页面上的更改。我想从上述方法中调用此功能,但由于该方法的同步性质,我无法使用回调。

在这种情况下,我如何“等待” jQuery.Deferred 的结果?

这里有一些伪代码(作为fiddle):

function saveAll() {
    var d = $.Deferred();

    //Do work asynchronously and cann d.resolve(true);
    setTimeout(function(){
        console.log("everything is saved.")
        d.resolve(true);
    }, 1000);

  return d;
}

function saveOnNavigation() {
    if(saveAll()){
        console.log("saved and navigate to next page.");
        return true;
    }
    console.log("failed to save. Staying on page");
    return false;
}

var x = saveOnNavigation();
if(x === true) {
    console.log("navigating to next page");
}

现在的输出是:

saved and navigate to next page.
navigating to next page
everything is saved.

虽然应该是:

everything is saved.
saved and navigate to next page.
navigating to next page

【问题讨论】:

  • 首先,我不知道小提琴不再被允许。其次,我并没有试图绕过规则,我只是想很好地格式化输出。第三,这些都不是重复的,因为我不能将 async: false 放在我的 saveAll 方法中(这就是为什么我在我的问题中从未谈论过 ajax)
  • 小提琴是允许的,但问题的全部内容必须在问题中in。 Fiddles 可以作为辅助工具(尽管 Stack Snippets 没有太多用例),但不能替代。
  • 我理解(并支持)关于小提琴的规则,我会在以后的问题中添加代码。我添加了有关如何调用代码的说明。
  • 我假设&lt;a href="link" onclick="saveOnNavigation"&gt;link&lt;/a&gt; 真的是&lt;a href="link" onclick="saveOnNavigation()"&gt;link&lt;/a&gt; 或类似的(注意())?或者实际上,鉴于您使用的是返回值,它必须是 &lt;a href="link" onclick="return saveOnNavigation()"&gt;link&lt;/a&gt;?

标签: jquery asynchronous jquery-deferred


【解决方案1】:

你还没有展示你是如何调用这些函数的,但如果它归结为onbeforeunload 或onunload 处理程序,恐怕你不能按照你的要求去做。这些都不能等待异步过程完成,也不能取消(用户除外)。 见下文,你可以用这些做点什么链接。

另外:在最近的 jQuery 中,没有办法使 $.Deferred 的回调同步到您的 then(done 等)处理程序(即使在旧 jQuery 中,如果 Deferred在您致电then 等之前尚未解决)。 jQuery 的Deferred 已更新为与Promises/A+ specification 一致,这要求对then 的调用和类似的回调必须是异步的。 (在更新为符合该规范之前,Deferred 是混乱的:如果已经解决,它会同步调用回调,但如果还没有,那么它当然是异步的。Promises/A+ 没有不允许这种混乱的行为。)


您现在已经说过要像这样调用这些函数:

<a href="link" onclick="saveOnNavigation">link</a>

我认为它必须如下,()

<a href="link" onclick="saveOnNavigation()">link</a>

如果您可以将其更改为:

<a href="link" onclick="return saveOnNavigation(this, event)">link</a>

然后您可以继续,在该处理程序中进行异步操作,取消默认导航,然后在完成后导航:

function saveOnNavigation(link, event) {
    // Start the async operation
    startAsynchronousOperation().then(function(saved) {
        // Now it's finished (later)
        if (saved){
            // All good, do the navigation
            console.log("saved and navigate to next page.");
            location = link.href;
        } else {
            // Not good, don't navigate
            console.log("failed to save. Staying on page");
        }
    });

    // Cancel the click (belt-and-braces)
    if (event.preventDefault) {
        event.preventDefault();
    }
    return false;
}

实时示例:

var clicks = 0;

function startAsynchronousOperation() {
  var d = $.Deferred();
  setTimeout(function() { // Emulate an async op
    if (++clicks == 1) {
      d.resolve(false);   // disallow
    } else {
      d.resolve(true);    // allow
    }
  }, 500);
  return d.promise();
}
function saveOnNavigation(link, event) {
  // Start the async operation
  startAsynchronousOperation().then(function(saved) {
    // Now it's finished (later)
    if (saved) {
      // All good, do the navigation
      console.log("saved and navigate to next page.");
      location = link.href;
    } else {
      // Not good, don't navigate
      console.log("failed to save. Staying on page");
    }
  });

  // Cancel the click (belt-and-braces)
  if (event.preventDefault) {
    event.preventDefault();
  }
  return false;
}
<a href="http://example.com" onclick="return saveOnNavigation(this, event)">The first click on this link will be stopped; the second one will be allowed.</a>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

注意:如果用户右键单击链接并从菜单中选择其中一个选项(例如“在新选项卡中打开”),您的 onclick 处理程序将不会被调用上面的代码都不起作用。但我猜,既然这不会使当前窗口离开页面,那没关系。

如果您不能更改页面的标记,我们仍然可以做到。 :-) 我们只需要在页面加载后运行一些代码,将那些老式的属性样式事件处理程序转换为现代事件处理程序:

$('a[onclick*="saveOnNavigation"]')
    .attr("onclick", "")
    .on("click", saveOnNavigation);

...我们将saveOnNavigation 更改为:

function saveOnNavigation(event) {
  var link = this;

  // Cancel the click
  event.preventDefault();

  // Start the async operation
  startAsynchronousOperation().then(function(saved) {
    // Now it's finished (later)
    if (saved) {
      // All good, do the navigation
      console.log("saved and navigate to next page.");
      location = link.href;
    } else {
      // Not good, don't navigate
      console.log("failed to save. Staying on page");
    }
  });
}

例子:

var clicks = 0;

function startAsynchronousOperation() {
  var d = $.Deferred();
  setTimeout(function() { // Emulate an async op
    if (++clicks == 1) {
      d.resolve(false);   // disallow
    } else {
      d.resolve(true);    // allow
    }
  }, 500);
  return d.promise();
}
function saveOnNavigation(event) {
  var link = this;

  // Cancel the click
  event.preventDefault();

  // Start the async operation
  startAsynchronousOperation().then(function(saved) {
    // Now it's finished (later)
    if (saved) {
      // All good, do the navigation
      console.log("saved and navigate to next page.");
      location = link.href;
    } else {
      // Not good, don't navigate
      console.log("failed to save. Staying on page");
    }
  });
}
$(document).ready(function() { // Don't need this if the script tag is at the end, just before </body>
    $('a[onclick*="saveOnNavigation"]')
        .attr("onclick", "")
        .on("click", saveOnNavigation);
});
<a href="http://example.com" onclick="saveOnNavigation()">The first click on this link will be stopped; the second one will be allowed.</a>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

【讨论】:

  • 所以没有 jQuery 等价于 .net 的 task.Result?
  • @Lodewijk:不,JavaScript 的 run-to-completion 语义不允许这样做。不过,我已经更新了上述内容,以根据您调用 saveOnNavigation 函数的方式提供解决方案。
  • 感谢您的选择。当然,实际代码比我的示例要复杂一些。我会看看我是否可以让你的一些例子起作用。
  • 接受了您的回答,但最终我说服我的经理更改此功能,以便我们可以使用onbeforeunload。
【解决方案2】:

试试jquery的$.when().then()

var d = $.Deferred();
var myvariable = false;
$(document).ready(function(){
    saveAll()
})

function saveAll() {
    //var d = $.Deferred();

    //Do work asynchronously and cann d.resolve(true);
  myvariable = setTimeout(function(){
    console.log("everything is saved.")
    d.resolve();
    myvariable = true;

  }, 1000);


}
$.when(d).then(function(){
    var x = saveOnNavigation();
if(x === true) {
    console.log("navigating to next page");
}
})



function saveOnNavigation() {
    if(myvariable){
    console.log("saved and navigate to next page.");
    return true;
  }
  console.log("failed to save. Staying on page");
  return false;
}

//

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-20
    • 2016-12-05
    • 2018-08-28
    • 1970-01-01
    • 1970-01-01
    • 2016-05-18
    • 1970-01-01
    相关资源
    最近更新 更多