JQuery延迟:同步等待结果
JQuery Deferred: wait for result synchronously
我有一个函数,当用户离开页面时调用该函数。此功能可确保始终保存页面上的未决更改。如果保存失败,可以通过从该方法返回 false
来取消导航。这是应用程序中的现有代码,无法更改以使用回调(旧版)。
此方法用于导航元素的 OnClick 处理程序(我知道,遗留):
<a href="link" onclick="return saveOnNavigation()">link</a>
现在我有了使用 jQuery.Deferred 保存页面更改的新功能。我想从上述方法调用此功能,但由于该方法的同步性质,我无法使用回调。
在这种情况下,我如何 'wait' 获得 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
你没有说明你是如何调用这些函数的,但如果它归结为 onbeforeunload
或 onunload
处理程序,恐怕你可以'不要做你要求的事。这些都不能等待异步过程完成,也不能取消(用户除外)。 见下文,你也许可以用那些 links 做点什么。
另外:在最近的 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>
注意: 如果用户右键单击 link 并从菜单中选择一个选项(例如 "open in new tab"),您的 onclick
处理程序将不会被调用,上面代码的 none 将起作用。但我猜,因为这不会使当前 window 离开页面,所以没关系。
如果您不能更改页面的标记,我们仍然可以这样做。 :-) 我们只需要在页面加载后 运行 一些代码,将那些老式的属性式事件处理程序转换为现代事件处理程序:
$('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
的 $.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;
}
//
我有一个函数,当用户离开页面时调用该函数。此功能可确保始终保存页面上的未决更改。如果保存失败,可以通过从该方法返回 false
来取消导航。这是应用程序中的现有代码,无法更改以使用回调(旧版)。
此方法用于导航元素的 OnClick 处理程序(我知道,遗留):
<a href="link" onclick="return saveOnNavigation()">link</a>
现在我有了使用 jQuery.Deferred 保存页面更改的新功能。我想从上述方法调用此功能,但由于该方法的同步性质,我无法使用回调。
在这种情况下,我如何 'wait' 获得 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
你没有说明你是如何调用这些函数的,但如果它归结为 见下文,你也许可以用那些 links 做点什么。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>
注意: 如果用户右键单击 link 并从菜单中选择一个选项(例如 "open in new tab"),您的 onclick
处理程序将不会被调用,上面代码的 none 将起作用。但我猜,因为这不会使当前 window 离开页面,所以没关系。
如果您不能更改页面的标记,我们仍然可以这样做。 :-) 我们只需要在页面加载后 运行 一些代码,将那些老式的属性式事件处理程序转换为现代事件处理程序:
$('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
的 $.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;
}
//