在很多应用场景中,我们会遇到通过ajax来获取值。一般来说,我们会直接在ajax的回调函数里边直接进行处理,但是在某些场景中需要将ajax的返回值传递出来,本文介绍3种可用的方法。每种方法都有自己的优缺点,读者可以自行考虑使用其中的一种。
先展示一下我们原始的函数,方便后文的描述:
function GetThings()
{
$.post("XX.php",
function(data){
return data;
});
}
为了方便读者能够跑起来,其XX.php内容可以如下(当然本地需要配置相关环境使之能够跑php文件)
echo "HelloWorld!";
*事实上,也可以直接写一个记事本文档(.txt),这样就不需要配置php环境了。
我们是希望通过这个GetThings()函数获取到某些数据,然后将其返回回来。
方法1:同步
我们知道,对于jQuery封装的$.post、$.get等方法默认都是异步传输的,所以如果对上面的GetThings()方法来说,执行 a = GetThings();是无法将ajax的返回值返回给a。解决方法就是将GetThings()方法改成同步:
function GetThings()
{
$.ajax({
url:"XX.php",
async:false,
type:"POST",
success:function(data){
return data;
}
});
}
此时通过a = GetThings();访问,发现还是没有返回值。原因在于,虽然改为同步了,但是其实GetThings()本身并没有返回值,所以最终的结果还是空,解决方案是将$.ajax的返回值保存起来返回回来:
function GetThings()
{
var t = undefined;
$.ajax({
url:"XX.php",
async:false,
type:"POST",
success:function(data){
t = data;
}
});
return t;
}
这样就可以通过a = GetThings();访问到最终的值了(本例中即”Hello World!”)。稍微提醒一下的是,网上看到几篇文章对于同步给出了如下的代码,但是我测试不通过(本地采用jquery1.7.1的版本):
$.post("XX.php",{async:false},function(){...});
至此,该方法解决了接受ajax返回值的问题,其优点在于简单明了,理论上支持jQuery的各个版本;调用方式(a = GetThings();)符合最常规的逻辑。缺点在于,由于是同步,导致浏览器在执行GetThings()时处于一个浏览器假死的状态——直到数据返回才能继续执行该语句后面的代码,这意味着如果数据一直不返回,那么浏览器会一直等待数据,于是就一直在等待,此时浏览器就处于无响应状态。
方法2:回调函数
此方法思想就很简单了,实现起来也比较方便,直接上代码:
function GetThings(callback)
{
$.post("XX.php",
function(data){
callback(data);
});
}
此时,如果要接收返回的值,只需要在callback里边完成就行,具体的来说就是:
GetThings(function(data){
t = data;
});
另外一个例子可以参看参考资料1。该方法简单明了,不存在浏览器卡死之类的情况,并且理论上支持所有版本的jQuery,比起方法1可能就调用时没有那么直观,其他没有什么缺点。
方法3:Deferred对象
这个方法相对前两个方法来说比较高级,但是思路比较简单,就是采用Deferred对象。
Deferred对象,简单说,Deferred对象就是jQuery的回调函数解决方案。我们经常遇到某些耗时很长的javascript操作。其中,既有异步的操作(比如ajax读取服务器数据),也有同步的操作(比如遍历一个大型数组),它们都不是立即能得到结果的。通常的做法是,为它们指定回调函数(callback)。即事先规定,一旦它们运行结束,应该调用哪些函数。但是,在回调函数方面,jQuery的功能非常弱。为了改变这一点,jQuery开发团队就设计了Deferred对象。在英语中,defer的意思是”延迟”,所以deferred对象的含义就是”延迟”到未来某个点再执行。Deferred对象从jQuery1.5开始引入,并且jQuery团队改写了诸如ajax等方法,在1.5之前,$.ajax会返回XHR对象;而则之后,返回一个Deferred对象。比如原来的$.post方法可以像前文描述的方式写:
$.post("XX.php",
function(data){
callback(data);
});
如果你的jQuery版本>=1.5可以采用如下方式:
$.post("XX.php").done(function(data){
callback(data);
});
即可以把回调函数拿出来放在最后边。因为$.post返回Deferred对象,而该对象支持链式方法,其.done()方法表示前者已完成时的处理(Deferred对象有3种状态,即未完成,已完成和已失败)。对于Deferred对象可以参见参考资料2,该文章很清晰地解释了该对象,当然也可以参看官方文档给出的信息。
我们原始的设想是,希望在GetThings()执行完之后将其值拿出来。Deferred对象能够检测到自身的完成状态,并在完成时继续执行完成后的代码,此处修改原始方法如下:
function GetThings()
{
var def = $.Deferred();
$.post("XX.php",
function(data){
return def.resolve(data);
}
});
return def.promise();
}
此处在GetThings()中新建了一个Deferred对象,同时该函数返回了该Deferred.promise()。该方法是记录该Deferred对象是否已经完成,而Deferred.resolve()将手动修改该对象为已完成状态。当然如果你愿意可以把该函数按照前文所述改成链式:
$.post("XX.php").done(function(data{callback(data);});
由于GetThings()返回了一个Deferred对象,所以要获取其数据可以链式地用.done()方法调用:
GetThings().done(function(data){
var t = data;
});
该方法的缺点在于对开发者要求相对较高,必须了解Deferred对象,而且该方法仅适用于jQuery1.5及以上版本,但是jQuery团队发明了该对象,并且重写了ajax部分,可见其重要性,而且可以说是一个趋势。Deferred对象从某种意义上来说是对回调函数的一种优化,可以看到Deferred代码编写更符合人的一般思维。
参考资料