一、AJAX示例 AJAX全稱為“Asynchronous JavaScript And XML”(非同步JavaScript和XML) 是指一種創建互動式網頁應用的開發技術、改善用戶體驗,實現無刷新效果。 1.1、優點 不需要插件支持 優秀的用戶體驗 提高Web程式的性能 減輕伺服器和帶寬的負擔 1 ...
一、AJAX示例
AJAX全稱為“Asynchronous JavaScript And XML”(非同步JavaScript和XML) 是指一種創建互動式網頁應用的開發技術、改善用戶體驗,實現無刷新效果。
1.1、優點
不需要插件支持
優秀的用戶體驗
提高Web程式的性能
減輕伺服器和帶寬的負擔
1.2、缺點
瀏覽器對XMLHttpRequest對象的支持度不足,幾乎所有瀏覽器現在都支持
破壞瀏覽器“前進”、“後退”按鈕的正常功能,可以通過簡單的插件彌補
對搜索引擎的支持不足
1.3、jQuery AJAX示例
在HTML5中對原生的AJAX核心對象XMLHttpRequest進行升級,也就是XHR2,功能更加強大。
jQuery對AJAX封裝的非常好,這裡以簡單的商品管理為示例使用jQuery完成AJAX應用。
Product.java bean:
package com.gomall.bean; /*** * 產品 * * @author Administrator * */ public class Product { /** 編號 */ private int id; /** 名稱 */ private String name; /** 價格 */ private double price; /** 圖片 */ private String picture; /** 詳細 */ private String detail; @Override public String toString() { return "Product [id=" + id + ", name=" + name + ", price=" + price + ", picture=" + picture + ", detail=" + detail + "]"; } public Product(int id, String name, double price, String picture) { super(); this.id = id; this.name = name; this.price = price; this.picture = picture; } public Product(int id, String name, double price, String picture, String detail) { super(); this.id = id; this.name = name; this.price = price; this.picture = picture; this.detail = detail; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public String getPicture() { return picture; } public void setPicture(String picture) { this.picture = picture; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail; } }
IProductService.java:
package com.gomall.service; import java.util.List; import com.gomall.bean.Product; public interface IProductService { /**獲得所有*/ List<Product> getAll(); /**添加 * @return */ boolean add(Product entity); /**根據編號獲得產品對象*/ Product findById(int id); /**根據編號獲得產品對象 * @return */ boolean deleteById(int id); }
ProductService.java:
package com.gomall.service; import java.util.ArrayList; import java.util.List; import java.util.Random; import com.gomall.bean.Product; public class ProductService implements IProductService { public static ArrayList<Product> products; static { products = new ArrayList<>(); Random random = new Random(); for (int i = 1; i <= 10; i++) { Product product = new Product(i, "華為Mate9MHA-AL00/4GB RAM/全網通華為超級閃充技術雙後攝設計" + random.nextInt(999), random.nextDouble() * 1000, "pic(" + i + ").jpg", "產品詳細"); products.add(product); } } /* * (non-Javadoc) * * @see com.gomall.service.IProductService#getAll() */ @Override public List<Product> getAll() { return products; } /* * (non-Javadoc) * * @see com.gomall.service.IProductService#add(com.gomall.bean.Product) */ @Override public boolean add(Product entity) { try { entity.setId(products.size() + 1); entity.setPicture("pic(" + entity.getId() + ").jpg"); // uploadify // 上傳圖片 products.add(entity); } catch (Exception e) { e.printStackTrace(); return false; } return true; } /* * (non-Javadoc) * * @see com.gomall.service.IProductService#findById(int) */ @Override public Product findById(int id) { for (Product product : products) { if (product.getId() == id) { return product; } } return null; } /* * (non-Javadoc) * * @see com.gomall.service.IProductService#deleteById(int) */ @Override public boolean deleteById(int id) { try { Product product = findById(id); if (product != null) { products.remove(product); } } catch (Exception e) { e.printStackTrace(); return false; } return true; } }
ProductAction.java:
package com.gomall.action; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.codehaus.jackson.map.ObjectMapper; import com.gomall.bean.Product; import com.gomall.service.IProductService; import com.gomall.service.ProductService; @WebServlet("/Product") public class ProductAction extends HttpServlet { private static final long serialVersionUID = 1L; public ProductAction() { super(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /*模擬網路延時*/ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } request.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8"); String act = request.getParameter("act"); IProductService productService = new ProductService(); /**用於序列化json*/ ObjectMapper mapper = new ObjectMapper(); PrintWriter out=response.getWriter(); if (act.equals("getAll")) { String json = mapper.writeValueAsString(productService.getAll()); out.append(json); } else if (act.equals("area")) { String callback=request.getParameter("callback"); out.append(callback+"('"+new Date()+"')"); } else if (act.equals("getJSONP")) { String callback=request.getParameter("callback"); String json = mapper.writeValueAsString(productService.getAll()); out.append(callback+"("+json+")"); } else if (act.equals("getAllCORS")) { /**向響應的頭部中添加CORS信息*/ response.addHeader("Access-Control-Allow-Origin", "*"); response.addHeader("Access-Control-Allow-Methods", "GET,POST"); String json = mapper.writeValueAsString(productService.getAll()); out.append(json); } else if(act.equals("del")){ /**向響應的頭部中添加CORS信息*/ response.addHeader("Access-Control-Allow-Origin", "*"); response.addHeader("Access-Control-Allow-Methods", "GET,POST"); int id=Integer.parseInt(request.getParameter("id")); String json = mapper.writeValueAsString(productService.deleteById(id)); out.append(json); } else if(act.equals("add")){ /**向響應的頭部中添加CORS信息*/ response.addHeader("Access-Control-Allow-Origin", "*"); response.addHeader("Access-Control-Allow-Methods", "GET,POST"); String name=request.getParameter("name"); double price=Double.parseDouble(request.getParameter("price")); String detail=request.getParameter("detail"); Product entity=new Product(0, name, price, "",detail); String json = mapper.writeValueAsString(productService.add(entity)); out.append(json); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
運行結果:
刪除:
二、延遲對象(Deferred)
deferred對象就是jQuery1.5版以後新增加的回調函數解決方案。
2.1、回調函數
先看一個示例:
首先,為什麼要使用Deferred?
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>回調</title> </head> <body> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> var student; $.get("student.json",function(data){ student=data; },"json"); alert(student); </script> </body> </html>
student.json文件:{"name":"tom","id":"01"}
運行結果:
因為AJAX是非同步執行的,類似高級語言中的多線程,當發起ajax請求時會有網路延遲,而代碼並沒有在$.get的位置被阻塞,alert先執行,但數據並沒有從遠程獲取到,所以結果是undefined。
其實初學者經常會犯這種錯誤,如:
function getStudentById(id){ $.get("students.do",{"id":id},function(stu){ return stu; },"json"); }
上面的代碼是有問題的,原因如前面的示例是一樣的。怎麼解決,如果你認為是非同步帶來的問題,當然通過同步是可以解決的,如:
$.ajax({ type:"get", url:"student.json", async:false, /*非非同步,同步*/ success:function(data){ student=data; } });
結果:
如果將所有的ajax請求修改為同步的,則ajax的好處就大打折扣了,如果即要非同步又要解決上面的問題,可以使用回調方法。
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>回調</title> </head> <body> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> /*callback是一個當ajax請求成功時的回調方法*/ function getStudent(callback) { $.get("student.json", function(data) { callback(data); }, "json"); } /*調用getStudent函數,傳入參數,參數也是一個函數*/ getStudent(function(stu){ alert(stu.id); }); /*把一個方法作為參數傳遞給另一個方法可以認為是委托,如C++中的函數指針類似*/ getStudent(function(stu){ alert(stu.name); }); </script> </body> </html>
結果:
從這裡看回調很完美,其實不然,實際開發中要複雜得多,如當第一個ajax請求完成才可以完成第二個,當第二個完成才可以完成第三個,可能最一個請求要等前面的所有請求都成功時才允許執行或才有條件執行,如
使用ajax編輯用戶信息,先載入用戶對象,再載入省,載入市,加縣,可能代碼會這樣寫:
$.get("url1",function(){ $.get("url2",function(){ $.get("url3",function(){ ... }); }); });
當回調越來越多,嵌套越深,代碼可讀性就會越來越差。如果註冊了多個回調,那更是一場噩夢,幸好從jQuery1.5開始出現了延遲對象(deferred),可以解決這個問題。
2.2、deferred.done
$.ajax()操作完成後,如果使用的是低於1.5.0版本的jQuery,返回的是XHR對象,你沒法進行鏈式操作;如果高於1.5版,返回的是deferred對象,可以進行鏈式操作。
當延遲成功時調用一個函數或者數組函數,功能與原success類似。
語法:deferred.done(doneCallbacks[,doneCallbacks])
返回值:Deferred Object
該參數可以是一個函數或一個函數的數組。當延遲成功時,doneCallbacks被調用。回調執行是依照他們添加的順序。一旦deferred.done()返回延遲對象,延遲對象的其它方法也可以鏈接到了這裡,包括增加.done()方法。當延遲解決,doneCallbacks執行使用參數提供給 resolve或 resolveWith方法依照添加的順序調用。
示例代碼:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>延遲對象(deferred)</title> </head> <body> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> console.log("使用方法一"); $.get("student.json", "json").done(function(stu) { console.log(stu.id); }).done(function(stu) { console.log(stu.name); }); console.log("使用方法二"); $.get("student.json", "json").done(function(stu) { console.log(stu.id); }, function(stu) { console.log(stu.name); }); </script> </body> </html>
運行結果:
2.3、deferred.fail
語法:deferred.fail(failCallbacks[,failCallbacks])
返回值:Deferred Object
當延遲失敗時調用一個函數或者數組函數,功能與原回調方法error類似。
該參數可以是一個函數或一個函數的數組。當延遲失敗時,doneCallbacks被調用。回調執行是依照他們添加的順序。一旦deferred.fail()返回延遲對象,延遲對象的其它方法也可以鏈接到了這裡,包括增加.done()方法。當延遲解決,doneCallbacks執行使用參數提供給 resolve或 resolveWith方法依照添加的順序調用。
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>延遲對象(deferred)</title> </head> <body> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $.get("stu.json", "json").done(function(stu) { console.log(stu.name); }).fail(function(xhr, status, errorThrown){ alert("xhr:"+xhr+",status:"+status+",errorThrown:"+errorThrown); }); </script> </body> </html>
運行結果:
2.4、deferred.always
語法:deferred.always(alwaysCallbacks,[alwaysCallbacks])
返回值:Deferred Object
當遞延對象是解決(成功,resolved)或拒絕(失敗,rejected)時被調用添加處理程式,與回調方法complete類似。
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>延遲對象(deferred)</title> </head> <body> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $.get("student.j", "json").done(function(stu) { console.log(stu.name); }).fail(function(data, status, errorThrown){ console.log("data:"+data+",status:"+status+",errorThrown:"+errorThrown); }).always(function(data, textStatus){ console.log("ajax執行完成,完成狀態:"+textStatus); }); </script> </body> </html>
運行結果
成功時:
失敗時:
2.5、deferred.then
deferred.then(doneFilter [, failFilter ] [, progressFilter ])
添加處理程式被調用時,遞延對象得到解決或者拒絕,一次指定多個事件。
所有三個參數(包括progressCallbacks ,在jQuery的1.7 )可以是一個單獨的函數或一個函數的數組。 其中一個參數,也可以為空,如果沒有該類型的回調是需要的。或者,使用.done()或.fail()僅設置doneCallbacks或failCallbacks。當遞延解決,doneCallbacks被調用。若遞延代替拒絕,failCallbacks被調用。回調按他們添加的順序執行。一旦deferred.then返回延遲對象,延遲對象的其它方法也可以鏈接到了這裡,包括增加.then()方法。
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>延遲對象(deferred)</title> </head> <body> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> $.get("student.jsonx", "json").then(function(stu) { console.log(stu.name); }, function(data, status, errorThrown) { console.log("data:" + data + ",status:" + status + ",errorThrown:" + errorThrown); }); </script> </body> </html>
結果:
2.6、應用延遲對象
前面的示例中我們都是使用jQuery ajax返回的deferred對象,其實我們也可以在自定義的代碼中使用deferred對象,恰當的使用deferred對象或以優雅的解決不少問題。
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>延遲對象(deferred)</title> </head> <body> <script src="js/jQuery1.11.3/jquery-1.11.3.min.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> var myTask=function(){ //通過jQuery創建一個延遲對象 var def=$.Deferred(); setTimeout(function(){ //隨機產生一個整數,如果是偶數 var n=Math.round(Math.random()*100); if(n%2==0) { console.log("一個耗時的操作終於完成了,n="+n); def.resolve(); //任務成功完成 }else{ console.log("一個耗時的操作失敗,n="+n); def.reject(); //拒絕,失敗 } },3000); //返回延遲對象,防止中間修改 return def.promise(); } /*當方法myTask執行完成後,添加回調方法*/ $.when(myTask()).done(function(){ console.log("執行成功"); }).fail(function