学习AJAX(2)
学习要点:通过学习,运用之前学过的知识,自己封装一个AJAX。
在以前的一篇博文中,展示了如何把代码封装起来,使用起来更顺手,结构更简洁。
现在我们就来对上一篇博文中的AJAX进行封装。
之前的代码:
let request = new XMLHttpRequest()
request.onreadystatechange = function(){
// 通信成功时,状态值为4
if (request.readyState === 4){
if (request.status === 200){
console.log(request.responseText);
let string = request.responseText
//把符合JSON语法的字符串转换成JS对应的值
let obj = window.JSON.parse(string)
console.log(obj)
} else {
console.error(request.statusText);
}
}
};
request.open('GET','http://www.baidu.com:8081')
request.send()
实现jQuery.ajax
把请求路径、请求方式、请求体、请求成功或者失败后执行的函数都分离出来,通过参数的形式传入我们定义的window.jQuery.ajax
函数中调用。
window.jQuery = function(node){
let nodes = {
0: node,
length: 1
}
return {
addClass: function(){
}
}
}
window.jQuery.ajax = function(method,url,body,successFn,failFn){
var xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.onreadystatechange = function(){
// 通信成功时,状态值为4
if (xhr.readyState === 4){
if (xhr.status >= 200 && xhr.status < 300){
successFn.call(undefined,xhr.responseText)
} else if(xhr.status >= 400) {
failFn.call(undefined,xhr.request)
}
}
}
xhr.send(body)
}
window.$ = window.jQuery
myButton.addEventListener('click',function(){
window.jQuery.ajax(
'GET',
'/xxx',
'a=1&b=2',
(responseText) => { console.log(responseText) },
(request) => { console.log(request) }
)
})
出现问题:如果是通过这种方式把url等参数传进去的话,有很大概率会出错。
比如,当我们的参数少传,或者是顺序出错,那代码就会报错。
所以,我们可以使用对象,来把这些参数装起来。
优化代码
改进:为了让参数更简洁易懂。
window.jQuery.ajax = function(options){
let method = options.method
let url = options.url
let body = options.body
let successFn = options.successFn
let failFn = options.failFn
var xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.onreadystatechange = function(){
// 通信成功时,状态值为4
if (xhr.readyState === 4){
if (xhr.status >= 200 && xhr.status < 300){
successFn.call(undefined,xhr.responseText)
} else if(xhr.status >= 400) {
failFn.call(undefined,xhr.request)
}
}
}
xhr.send(body)
}
window.$ = window.jQuery
myButton.addEventListener('click',function(){
window.jQuery.ajax(
{
url:'/xxx',
method:'GET',
body:'a=1&b=2',
successFn:(responseText) => { console.log(responseText) },
failFn:(request) => { console.log(request) },
}
)
})
代码还是有点难看,我们使用一个ES6的新特性解构赋值吧~
解构赋值语法是一个 Javascript 表达式,这使得可以将值从数组或属性从对象提取到不同的变量中。
window.jQuery.ajax = function({url,method,body,successFn,failFn,header}){
/* 等同于:
let {url,method,body,successFn,failFn,header} = options
let url = options.url
let method = options.method
...
*/
var xhr = new XMLHttpRequest()
xhr.open(method, url)
//设置header
for(let key in header){
let value = header[key]
xhr.setHeader(key,value)
}
xhr.onreadystatechange = function(){
// 通信成功时,状态值为4
if (xhr.readyState === 4){
if (xhr.status >= 200 && xhr.status < 300){
successFn.call(undefined,xhr.responseText)
} else if(xhr.status >= 400) {
failFn.call(undefined,xhr.request)
}
}
}
xhr.send(body)
}
window.$ = window.jQuery
function f1(responseText){}
function f2(responseText){}
myButton.addEventListener('click',function(){
window.jQuery.ajax(
{
url:'/xxx',
method:'GET',
//设置header
header:{
"content-type":"application/x-www-form-urlencoded",
"frank":"18"
},
body:'a=1&b=2',
// callback
// 如果要传入两个函数
successFn:(x) => {
f1.call(undefined,x)
f2.call(undefined,x)
},
failFn:(x) => { console.log(x) },
}
)
})
使用Promise
问题:window.jQuery.ajax
是我们自己封装的函数,当我们点击按钮,发送HTTP请求成功,我们将会使用successFn函数;如果失败,我们将会使用failFn函数。但是,这都是在我们已经了解这个函数中有这些回调函数的情况下,才会做的下一步的步骤。
如果有另外几个封装了AJAX方法的库同时在使用呢?我们根本不清楚这些库里设置的成功回调函数或失败回调函数是什么样子的。
比如这几个库分别是:
frank.ajax({
ifSuccess:function(){},
ifFail: function(){}
})
jack.ajax(null,null,null,null,successFn,failFn)
这些回调函数的主要问题在于:如果我们不查阅文档,根本不知道应该传什么函数。就像我们的函数window.jQuery.ajax
中,如果不知道他的成功回调函数为successFn,那么在给这个按钮添加事件时,根本就不会知道他叫successFn。
那么,我们就迫切需要一个可以统一这些回调函数的解决方案。
那就是Promise
他接受两个参数,第一个是resolve,第二个是reject,如果成功,就调用resolve;如果失败,就调用reject,再也没有存在什么successFn和failFn,结构变得清晰。
同时,他会返回一个Promise实例对象,我们可以通过他自带的then属性对结果进行进一步的操作。
在我们定义的window.jQuery.ajax
使用Promise:
window.jQuery.ajax = function({url,method,body,header}){
//window.jQuery.ajax的返回值是一个Promise实例对象,有then()属性
return new Promise(function(resolve,reject){
var xhr = new XMLHttpRequest()
xhr.open(method, url)
//设置header
for(let key in header){
let value = header[key]
xhr.setHeader(key,value)
}
xhr.onreadystatechange = function(){
// 通信成功时,状态值为4
if (xhr.readyState === 4){
if (xhr.status >= 200 && xhr.status < 300){
//如果成功,调用resolve
resolve.call(undefined,xhr.responseText)
} else if(xhr.status >= 400) {
// 如果失败,调用reject
reject.call(undefined,xhr.request)
}
}
}
xhr.send(body)
})
}
window.$ = window.jQuery
myButton.addEventListener('click',function(){
window.jQuery.ajax(
{
url:'/xxx',
method:'GET',
header:{
"content-type":"application/x-www-form-urlencoded",
"frank":"18"
},
body:'a=1&b=2',
}
).then(
(text) => {console.log(text)},
(request) => {console.log(request)}
)
})
通过Promise,就能妥善地处理我们需要的结果和回调函数了。
jQuery中的AJAX
使用jQuery中的AJAX方法:
myButton.addEventListener('click',function(e){
$.ajax({
url:'/xx',
method:'GET',
success:(x)=>{},
error:(x)=>{}
})
})
或者是:
function success(responseText){console.log(responseText)}
function fail(request){console.log(request)}
myButton.addEventListener('click',function(e){
//返回一个Promise实例对象
$.ajax({
url:'/xx',
method:'GET',
}).then(
//接受两个参数,成功执行第一个参数,失败则执行第二个参数
success,fail
)
})
或者是:
myButton.addEventListener('click',function(e){
$.ajax({
url:'/xx',
method:'GET',
}).then(
//接受两个参数,成功执行第一个参数,失败则执行第二个参数
(responseText) => {console.log(responseText)},
(request) => {console.log(request)}
)
})
Promise链式写法:
myButton.addEventListener('click',function(e){
$.ajax({
url:'/xx',
method:'GET',
}).then(
//接受两个参数,成功执行第一个参数,失败则执行第二个参数
(responseText) => {
console.log(responseText)
return responseText
},
(request) => {console.log(request)}
).then(
(x)=>{console.log(x)} //如果成功,会对上一次成功操作的结果(即return responseText)的值再次打印出来
(y)=>{console.log(y)}
)
})
注意:jQuery会自动把content-type为JSON的返回值字符串转化为对象