【整理】Ajax异步实现的几种方式总结

Ajax = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)

Ajax 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。

GET 还是 POST?

与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。
然而,在以下情况中,请使用 POST 请求:

  • 无法使用缓存文件(更新服务器上的文件或数据库)
  • 向服务器发送大量数据(POST 没有数据量限制)
  • 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠

异步 - true 或 false?

  • XMLHttpRequest 对象如果要用于 Ajax 的话,其 open() 方法的 async 参数必须设置为 true
  • 不推荐使用 async=false,但是对于一些小型的请求,也是可以的。
  • 请记住,JavaScript 会等到服务器响应就绪才继续执行。如果服务器繁忙或缓慢,应用程序会挂起或停止。
  • 注意:当您使用 async=false 时,请不要编写 onreadystatechange 函数 —— 把代码放到 send() 语句后面即可

每当 readyState 改变时,就会触发 onreadystatechange 事件。
readyState 属性存有 XMLHttpRequest 的状态信息。

下面是 XMLHttpRequest 对象的三个重要的属性:






















属性描述
onreadystatechange存储函数(或函数名),每当 readystate 属性改变时,就会调用该函数。
readystate存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。

  • 0: 请求未初始化

  • 1: 服务器连接已建立

  • 2: 请求已接收

  • 3: 请求处理中

  • 4: 请求已完成,且响应已就绪


status200: “ok”
404: 未找到页面

1 直接使用JavaScript的Ajax简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function showCustomer(str)
{
var xmlhttp;
if (str=="")
{
document.getElementById("txtHint").innerHTML="";
return;
}
if (window.XMLHttpRequest)
{
// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{
// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
document.getElementById("txtHint").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("GET","getcustomer.asp?q="+str,true);
xmlhttp.send();
}

showCustomer() 函数执行以下任务:

  • 检查是否已选择某个客户
  • 创建 XMLHttpRequest 对象
  • 当服务器响应就绪时执行所创建的函数
  • 把请求发送到服务器上的文件
  • 请注意我们向 url 添加了一个参数 q (带有输入域中的内容)

2 使用JavaScript对Ajax封装

此处参考了Snandy 的比较成熟完整的封装,包含了超时检查、异常处理、新API解析JSON的特性,推荐

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*******************************************************************************************************
*
* 对创建xhr对象可能出现的异常进行了处理
*
* 出现异常后failure的第二个参数msg被赋值为create xhr failed
*
*******************************************************************************************************/

/**
*
* 1,执行基本ajax请求,返回XMLHttpRequest
* Ajax.request(url,{
* async 是否异步 true(默认)
* method 请求方式 POST or GET(默认)
* type 数据格式 text(默认) or xml or json
* encode 请求的编码 UTF-8(默认)
* timeout 请求超时时间 0(默认)
* data 请求参数 (字符串或json)
* success 请求成功后响应函数 参数为text,json,xml数据
* failure 请求失败后响应函数 参数为xmlHttp, msg, exp
* });
*
* 2,执行ajax请求,返回纯文本
* Ajax.text(url,{
* ...
* });
*
* 3,执行ajax请求,返回JSON
* Ajax.json(url,{
* ...
* });
*
* 4,执行ajax请求,返回XML
* Ajax.xml(url,{
* ...
* });
*/

Ajax =
function(){
function request(url,opt){
function fn(){}
opt = opt || {};
var async = opt.async !== false,
method = opt.method || 'GET',
type = opt.type || 'text',
encode = opt.encode || 'UTF-8',
timeout = opt.timeout || 0,
data = opt.data || null,
success = opt.success || fn,
failure = opt.failure || fn;
method = method.toUpperCase();
if(data && typeof data == 'object'){//对象转换成字符串键值对
data = _serialize(data);
}
if(method == 'GET' && data){
url += (url.indexOf('?') == -1 ? '?' : '&') + data;
data = null;
}
var xhr = function(){
try{
return new XMLHttpRequest();
}catch(e){
try{
return new ActiveXObject('Msxml2.XMLHTTP');
}catch(e){
try{
return new ActiveXObject('Microsoft.XMLHTTP');
}catch(e){
failure(null,'create xhr failed',e);
}
}
}
}();
if(!xhr){return;}
var isTimeout = false, timer;
if(async && timeout>0){
timer = setTimeout(function(){
xhr.abort();
isTimeout = true;
},timeout);
}
xhr.onreadystatechange = function(){
if (xhr.readyState == 4 && !isTimeout){
_onStateChange(xhr, type, success, failure);
clearTimeout(timer);
}else{}
};
xhr.open(method,url,async);
if(method == 'POST'){
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded;charset=' + encode);
}
xhr.send(data);
return xhr;
}
function _serialize(obj){
var a = [];
for(var k in obj){
var val = obj[k];
if(val.constructor == Array){
for(var i=0,len=val.length;i<len;i++){
a.push(k + '=' + encodeURIComponent(val[i]));
}
}else{
a.push(k + '=' + encodeURIComponent(val));
}
}
return a.join('&');
}
function _onStateChange(xhr,type,success,failure){
var s = xhr.status, result;
if(s>= 200 && s < 300){
switch(type){
case 'text':
result = xhr.responseText;
break;
case 'json':
result = function(str){
return (new Function('return ' + str))();
}(xhr.responseText);
break;
case 'xml':
result = xhr.responseXML;
break;
}
success(result);
}else if(s===0){
failure(xhr,'request timeout');
}else{
failure(xhr,xhr.status);
}
xhr = null;
}
return (function(){
var Ajax = {request:request}, types = ['text','json','xml'];
for(var i=0,len=types.length;i<len;i++){
Ajax[types[i]] = function(i){
return function(url,opt){
opt = opt || {};
opt.type = types[i];
return request(url,opt);
}
}(i);
}
return Ajax;
})();
}();

3 ASP.NET中的Ajax控件

微软很贴心地为开发者封装了一套傻瓜化的Ajax控件,拖拖控件就可以实现Ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<p>
<asp:Label ID="lblNonAjax" runat="server" Text="No Ajax"></asp:Label>
</p>
<hr />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Label ID="lblText" runat="server"></asp:Label>
<p>
<asp:Button ID="btnAjax" runat="server" Text="Ajax Event"
onclick="btnAjax_Click"/>
<asp:Button ID="btnNonAjax" runat="server" Text="Post Black Event"
onclick="btnNonAjax_Click"/>
</p>
</ContentTemplate>
<Triggers>
<asp:PostBackTrigger ControlID="btnNonAjax"/>
</Triggers>
</asp:UpdatePanel>
</div>
</form>

用法:

①一个页面只能有一个ScriptManager(脚本控制器)控件,并且放在其他Ajax控件的前面,它是ASP.NET Ajax的基础,用来处理页面上的所有Ajax组件以及页面局部更新,生成相应的客户端代理脚本
②在ScriptManager还可以设置超时处理、异常处理等属性,详见MSDN
③具体实现Ajax的部分放在UpdatePanel控件,可以在该控件里指定事件需要局部刷新还是整页刷新,默认是异步的局部刷新。如果要想实现整页刷新提交,像平时不用Ajax控件的效果,只需在<Triggers>标签里面指定<asp:PostBackTrigger ControlID="btnNonAjax" />,ControlID就是不需要异步提交的控件,如此指定后该控件的任何事件都是整页刷新提交

接下来是对应的页面后台代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
//因为以下控件不在Update内,所以触发Ajax事件时无法对其进行更新
lblNonAjax.Text = "触发了回发事件!";
}
}

protected void btnAjax_Click(object sender, EventArgs e)
{
this.lblText.Text = "这是一个Ajax事件的触发";

//Response.Write("<script>alert('这是一个Ajax事件的触发')</script>");
//触发AJAX事件,不能使用该方式或其他方式向页面输出,除非使用下面的方式

//触发的是UpdatePanel里的控件的事件,输出脚本必须使用以下方式
ScriptManager.RegisterStartupScript(this.UpdatePanel1, this.GetType(), "ajaxScript", "alert('这是一个Ajax事件的触发');", true);
}

protected void btnNonAjax_Click(object sender, EventArgs e)
{
this.lblText.Text = "这是一个页面回发刷新事件的触发";
Response.Write("<script>alert('这是一个页面回发刷新事件的触发')</script>");
}
}

①由于这种类似于拖控件的方式生成的html代码量巨大,不灵活也不好控制细节,故现在基本上较少采用了
②如果需要实现回调功能,可以使用ClientScriptManager.GetCallbackEventReference方法配合JavaScript实现,详见MSDN

4 JQuery 实现Ajax

4.1 JQuery.get()

语法:$(selector).get(url,data,success(response,status,xhr),dataType)

参数url是必须的,其他可选

示例:

1
2
3
$.get("test.php", function(data){
alert("Data Loaded: " + data);
});

4.2 JQuery.post()

语法:jQuery.post(url,data,success(data, textStatus, jqXHR),dataType)

参数url是必须的,其他可选

示例:

1
2
3
4
$.post("test.php", { name: "John", time: "2pm" },
function(data){
alert("Data Loaded: " + data);
});

4.3 jQuery.ajax()

语法:jQuery.ajax([settings])

上面两个get()和post()方法都是ajax()方法的简化写法;参数settings是集合,可选

常用参数列表:(详见W3School






















































































参数名类型描述
urlString(默认值: 当前页地址)发送请求的地址。
typeString(默认值: “GET”)请求方式 (“POST” 或 “GET”), 默认为 “GET”。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。
timeoutNumber设置请求超时时间(毫秒)。此设置将覆盖全局设置。
asyncBoolean(默认值: true)默认设置下,所有请求均为异步请求。如果需要发送同步请求,请将此选项设置为 false。注意,同步请求将锁住浏览器,用户其它操作必须等待请求完成才可以执行。
beforeSendFunction发送请求前可修改 XMLHttpRequest 对象的函数,如添加自定义 http 头。XMLHttpRequest 对象是唯一的参数。如果返回 false 可以取消本次 Ajax 请求。
completeFunction请求完成后回调函数 (请求成功或失败之后均调用)。参数: XMLHttpRequest 对象和一个描述请求类型的字符串。
dataString发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。查看 processData 选项说明以禁止此自动转换。必须为 Key/Value 格式。如果为数组,jQuery 将自动为不同值对应同一个名称。如 {foo:[“bar1”, “bar2”]} 转换为 ‘&foo=bar1&foo=bar2’。
dataTypeString预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,比如 XML MIME 类型就被识别为 XML。在 1.4 中,JSON 就会生成一个 JavaScript 对象,而 script 则会执行这个脚本。随后服务器端返回的数据会根据这个值解析后,传递给回调函数。可用值:

  • “xml”: 返回 XML 文档,可用 jQuery 处理。

  • “html”: 返回纯文本 HTML 信息;包含的 script 标签会在插入 dom 时执行。

  • “script”: 返回纯文本 JavaScript 代码。不会自动缓存结果。除非设置了 “cache” 参数。注意:在远程请求时(不在同一个域下),所有 POST 请求都将转为 GET 请求。(因为将使用 dom 的 script标签来加载)

  • “json”: 返回 JSON 数据 。

  • “jsonp”: JSONP 格式。使用 JSONP 形式调用函数时,如 “myurl?callback=?” jQuery 将自动替换 ? 为正确的函数名,以执行回调函数。

  • “text”: 返回纯文本字符串


successFunction请求成功后的回调函数。参数:由服务器返回,并根据 dataType 参数进行处理后的数据;描述状态的字符串。
errorFunction(默认值: 自动判断 (XML 或 HTML))请求失败时调用此函数。有以下三个参数:XMLHttpRequest 对象、错误信息、(可选)捕获的异常对象。如果发生了错误,错误信息(第二个参数)除了得到 null 之外,还可能是 “timeout”, “error”, “notmodified” 和 “parsererror”。
cacheBoolean(默认值: true,datatype 为 script 和 jsonp 时默认为 false)设置为 false 将不缓存此页面。
jQuery 1.2 新功能。
contentTypeString(默认值: “application/x-www-form-urlencoded”)发送信息至服务器时内容编码类型。默认值适合大多数情况。如果你明确地传递了一个 content-type 给 $.ajax() 那么它必定会发送给服务器(即使没有数据要发送)。
globalBoolean是否触发全局 Ajax 事件。默认值: true。设置为 false 将不会触发全局 Ajax 事件,如 ajaxStart 或 ajaxStop 可用于控制不同的 Ajax 事件。
ifModifiedBoolean(默认值: false)仅在服务器数据改变时获取新数据。使用 http 包 Last-Modified 头信息判断。在 jQuery 1.4 中,它也会检查服务器指定的 ‘etag’ 来确定数据没有被修改过。
processDataBoolean(默认值: true)默认情况下,通过data选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),都会处理转化成一个查询字符串,以配合默认内容类型 “application/x-www-form-urlencoded”。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。

5 总结

方便实用选jQuery,性能优先选JavaScript封装。UpdatePanel?算了吧。。。

本文待补充

参考资料:
Ajax 基本封装
jQuery Ajax 实例 全解析
基于.NET三种Ajax技术的运用——原生JS、ASP.NET Ajax和.NET回调技术