注意:此答案写于 2010 年 2 月。
查看底部 2015、2016 和 2017 年的更新。
不能从异步函数返回任何内容。你能回报的是一个承诺。我在回答这些问题时解释了 promise 在 jQuery 中是如何工作的:
如果你能解释为什么你要返回数据,以及你以后想用它做什么,那么我也许能给你一个更具体的答案,如何做到这一点。
通常,而不是:
function testAjax() {
$.ajax({
url: "getvalue.php",
success: function(data) {
return data;
}
});
}
你可以像这样编写你的testAjax函数:
function testAjax() {
return $.ajax({
url: "getvalue.php"
});
}
然后你可以得到这样的承诺:
var promise = testAjax();
你可以存储你的 promise,你可以传递它,你可以在函数调用中将其用作参数,你可以从函数返回它,但是当你最终想要使用由AJAX调用返回的数据时,你必须这样做:
promise.success(function (data) {
alert(data);
});
(有关简化的语法,请参阅下面的更新。
如果此时您的数据可用,则将立即调用此函数。如果不是,则一旦数据可用,就会立即调用它。
执行所有这些操作的全部意义在于,您的数据在调用 $.ajax 后不会立即可用,因为它是异步的。Promises对于函数来说是一个很好的抽象:我不能给你返回数据,因为我还没有它,我不想阻止并让你等待,所以这里有一个承诺,你可以稍后使用它,或者只是把它交给别人并完成它。
请参阅此演示。
更新 (2015)
目前(截至 2015 年 3 月),jQuery Promises 与 Promises/A+ 规范不兼容,这意味着它们可能无法与其他 Promises/A+ 兼容实现很好地配合。
然而,在即将推出的3.x版本中,jQuery Promises将与Promises/A+规范兼容(感谢Benjamin Gruenbaum指出这一点)。目前(截至2015年5月),jQuery的稳定版本是1.x和2.x。
我上面(2011 年 3 月)解释的是一种使用 jQuery Deferred Objects 异步执行某些操作的方法,在同步代码中,这些操作可以通过返回值来实现。
但是同步函数调用可以做两件事 - 它可以返回一个值(如果可以的话)或抛出一个异常(如果它不能返回一个值)。Promises/A+ 以一种与同步代码中的异常处理几乎一样强大的方式解决了这两个用例。jQuery版本处理返回值的等效项很好,但复杂异常处理的等效项有些问题。
特别是,同步代码中异常处理的全部意义不仅仅是放弃一条漂亮的消息,而是尝试解决问题并继续执行,或者可能重新抛出相同或不同的异常以供程序的其他一些部分处理。在同步代码中,您有一个调用堆栈。在异步调用中,您不需要,并且 Promises/A+ 规范要求在 promise 内部进行高级异常处理可以真正帮助您编写代码,即使对于复杂的用例,也能以有意义的方式处理错误和异常。
有关 jQuery 与其他实现之间的差异,以及如何将 jQuery 承诺转换为 Promises/A+ 兼容,请参阅 Q 库 wiki 上的 Kris Kowal 等人的《来自 jQuery》和 Jake Archibald 在 HTML5 Rocks 上发表的 Promises 到达 JavaScript。
如何回报真正的承诺
我上面的例子中的函数:
function testAjax() {
return $.ajax({
url: "getvalue.php"
});
}
返回一个 jqXHR 对象,该对象是一个 jQuery 延迟对象。
要使它返回一个真正的承诺,你可以把它改成 - 使用Q wiki中的方法:
function testAjax() {
return Q($.ajax({
url: "getvalue.php"
}));
}
或者,使用 HTML5 Rocks 文章中的方法:
function testAjax() {
return Promise.resolve($.ajax({
url: "getvalue.php"
}));
}
这也是 promise
模块文档中解释的内容,它应该与 ES6 Promise.resolve() 一起使用
。Promise.resolve($.ajax(...))
要立即使用ES6 Promises,您可以使用Jake Archibald的es6-promise模块的polyfill()。
要查看在没有 polyfill 的情况下可以使用 ES6 Promise 的位置,请参阅:我可以使用:Promises。
有关详细信息,请参阅:
jQuery的未来
jQuery的未来版本(从3.x开始 - 截至2015年5月的当前稳定版本是1.x和2.x)将与Promises/A +规范兼容(感谢Benjamin Gruenbaum在评论中指出这一点)。“我们已经决定的两项更改是我们的延迟实现的承诺/ A +兼容性[...]”(jQuery 3.0 和 Web 开发的未来)。有关更多信息,请参阅:Dave Methvin 的 jQuery 3.0: The Next Generations 和 Paul Krill 的 jQuery 3.0: 更多的互操作性,更少的 Internet Explorer。
有趣的讲座
更新 (2016)
ECMA-262 第 6 版第 14.2 节中有一种称为箭头函数的新语法,可用于进一步简化上述示例。
使用 jQuery API,而不是:
promise.success(function (data) {
alert(data);
});
你可以写:
promise.success(data => alert(data));
或使用 Promises/A+ API:
promise.then(data => alert(data));
请记住始终将拒绝处理程序与以下任一项一起使用:
promise.then(data => alert(data), error => alert(error));
或与:
promise.then(data => alert(data)).catch(error => alert(error));
请参阅此答案,了解为什么应始终使用带有承诺的拒绝处理程序:
当然,在此示例中,你可以使用与回调相同的参数来调用,但是箭头语法更通用,可以让你编写如下内容:promise.then(alert)
alert
promise.then(data => alert("x is " + data.x));
并非每个浏览器都支持这种语法,但在某些情况下,您可以确定您的代码将在哪种浏览器上运行 - 例如,在编写Chrome扩展程序,Firefox附加组件或使用Electron,NW.js或AppJS的桌面应用程序时(有关详细信息,请参阅此答案)。
有关箭头函数的支持,请参阅:
更新 (2017)
现在有一种更新的语法称为异步函数,其中包含一个新关键字,而不是此代码:await
functionReturningPromise()
.then(data => console.log('Data:', data))
.catch(error => console.log('Error:', error));
让你写:
try {
let data = await functionReturningPromise();
console.log('Data:', data);
} catch (error) {
console.log('Error:', error);
}
您只能在使用关键字创建的函数中使用它。有关详细信息,请参阅:async
有关浏览器中的支持,请参阅:
有关 Node 中的支持,请参阅:
在你没有原生支持的地方,你可以使用Babel:async
await
或者使用稍微不同的语法,基于生成器的方法,如 in 或 Bluebird 协程:co
更多信息
有关更多详细信息的承诺的其他一些问题: