注意:此答案写于 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();
你可以存储你的承诺,你可以传递它,你可以在函数调用中将它用作参数,你可以从函数中返回它,但是当你最终想要使用你的数据时由 AJAX 调用返回,你必须这样做:
promise.success(function (data) {
alert(data);
});
(有关简化语法,请参阅下面的更新。)
如果此时您的数据可用,则将立即调用此函数。如果不是,则在数据可用时立即调用它。
执行所有这些操作的重点在于,您的数据在调用 $.ajax 后无法立即使用,因为它是异步的。 Promises 是函数的一个很好的抽象:我无法返回数据,因为我还没有数据,我不想阻止并让你等待,所以这里有一个 promise并且您以后可以使用它,或者只是将它交给其他人并使用它。
看到这个DEMO。
更新(2015 年)
目前(截至 2015 年 3 月)jQuery Promises 与 Promises/A+ specification 不兼容,这意味着它们可能无法与其他 Promises/A+ conformant implementations 很好地合作。
不过,即将发布的 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 Promise 转换为符合 Promises/A+ 标准,请参阅 Kris Kowal 等人的 Coming from jQuery。在 Q library wiki 上,Promises arrive in JavaScript by Jake Archibald 在 HTML5 Rocks 上。
如何兑现真正的承诺
我上面例子中的函数:
function testAjax() {
return $.ajax({
url: "getvalue.php"
});
}
返回一个 jqXHR 对象,它是一个 jQuery Deferred Object。
要让它返回一个真正的承诺,您可以将其更改为 - 使用 the method from the Q wiki:
function testAjax() {
return Q($.ajax({
url: "getvalue.php"
}));
}
或者,使用the method from the HTML5 Rocks article:
function testAjax() {
return Promise.resolve($.ajax({
url: "getvalue.php"
}));
}
这个Promise.resolve($.ajax(...)) 也是explained in the promise module documentation,它应该与ES6 Promise.resolve() 一起使用。
今天要使用 ES6 Promises,您可以使用 Jake Archibald 的 es6-promise module's polyfill()。
要查看在没有 polyfill 的情况下可以在哪里使用 ES6 Promises,请参阅:Can I use: Promises。
更多信息见:
jQuery 的未来
jQuery 的未来版本(从 3.x 开始 - 截至 2015 年 5 月的当前稳定版本是 1.x 和 2.x)将与 Promises/A+ specification 兼容(感谢 Benjamin Gruenbaum 在厘米)。 “我们已经决定的两个更改是我们的 Deferred 实现的 Promise/A+ 兼容性 [...]” (jQuery 3.0 and the future of Web development)。有关详细信息,请参阅:Dave Methvin 的 jQuery 3.0: The Next Generations 和 Paul Krill 的 jQuery 3.0: More interoperability, less Internet Explorer。
有趣的谈话
更新(2016 年)
ECMA-262, 6th Edition, Section 14.2 中有一个名为arrow functions 的新语法,可用于进一步简化上述示例。
使用 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));
并不是每个浏览器都支持这种语法,但在某些情况下,您可以确定您的代码将在哪个浏览器上运行 - 例如。在使用 Electron、NW.js 或 AppJS 编写 Chrome extension、Firefox Add-on 或桌面应用程序时(详见 this answer)。
箭头函数的支持见:
更新(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 中的支持,请参阅:
在你没有原生支持 async 和 await 的地方你可以使用 Babel:
或者在语法稍有不同的情况下使用基于生成器的方法,例如 co 或 Bluebird 协程:
更多信息
关于更多细节的一些其他问题: