JQuery Deferred:同步等待结果

时间:2016-11-25 13:09:10

标签: jquery asynchronous jquery-deferred

我有一个在用户离开页面时调用的函数。此功能可确保始终保存页面上的挂起更改。如果保存失败,可以通过从此方法返回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

2 个答案:

答案 0 :(得分:1)

您还没有显示您如何调用这些功能,但如果归结为onbeforeunloadonunload处理程序,我会害怕您不能做你所问的事。这些都不能等待异步过程完成,也不能取消(用户除外)。 见下文,你可以做些什么链接。

另外:在最近的jQuery中,没有办法让$.Deferred回调你的thendone等)处理程序同步(甚至在旧的jQuery中,如果在调用Deferred之前then尚未解决,则无法解决问题。 jQuery的Deferred已更新为与Promises/A+ specification一致,这要求对then和类似回调的调用必须是异步的。 (在更新为对该规范进行投诉之前,Deferred是混乱的:如果它已经解决了,它会同步调用回调,但如果它还没有,那么它当然是异步的。 / 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;
}

Live exmaple:

&#13;
&#13;
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;
}
&#13;
<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>
&#13;
&#13;
&#13;

注意:如果用户右键单击该链接并从菜单中选择其中一个选项(例如&#34;在新标签中打开&#34;),则{{1}处理程序不会被调用,上面的代码都不会起作用。但是我猜测,因为那不会使当前窗口离开页面,这没关系。

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

onclick

...我们将$('a[onclick*="saveOnNavigation"]') .attr("onclick", "") .on("click", saveOnNavigation); 更改为:

saveOnNavigation

示例:

&#13;
&#13;
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");
    }
  });
}
&#13;
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);
});
&#13;
&#13;
&#13;

答案 1 :(得分:-1)

尝试$ .when()。然后()jquery

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

//