一、简介
AJAX 全称为 Asynchronous JavaScript And XML
通过 AJAX 可以在浏览器中向服务器发送异步请求,最大的优势:无刷新获取数据。
AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。
二、AJAX优缺点:
2.1优点
-
可以无需刷新页面而与服务器端进行通信。
-
允许你根据用户事件来更新部分页面内容。
2.2缺点
-
没有浏览历史,不能回退
-
存在跨域问题(同源)
-
SEO不友好
三、AJAX使用
3.1创建XMLHttpRequest对象
var xhr = new XMLHttpRequest();
3.2设置请求信息
xhr.open(method, url,true);//true表示异步
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
3.3发送请求
xhr.send(body) //get 请求不传 body 参数,只有 post 请求使用
3.4接收响应
xhr.onreadystatechange = function (){
if(xhr.readyState == 4 && xhr.status == 200){
console.log(xhr.responseText);
}
}
3.5解决IE缓存问题
在一些浏览器中(IE),由于缓存机制的存在,ajax 只会发送的第一次请求,剩余多次请求不会在发送给浏览器而是直接加载缓存中的数据。
//解决方式:浏览器的缓存是根据url地址来记录的,所以只需要修改url地址 即可避免缓存问题
xhr.open("get","/testAJAX?t="+Date.now());
3.6AJAX的请求状态
xhr.readyState 可以用来查看请求当前的状态
- 0:表示
XMLHttpRequest实例已经生成,但是 open()方法还没有被调用。 - 1:表示 send()方法还没有被调用,仍然可以使用
setRequestHeader()设定 HTTP请求的头信息 - 2: 表示 send()方法已经执行,并且头信息和状态码已经收到。
- 3: 表示正在接收服务器传来的 body 部分的数据。
- 4: 表示服务器数据已经完全接收,或者本次接收已经失败了
四、异步和同步
- 同步:如果能直接拿到结果,则为同步
情景:在医院挂号,要拿到号才能离开窗口,不拿到结果不会离开
- 异步:不能直接拿到结果
情景:在餐厅门口等位,拿到号可以去逛街,每10分钟去餐厅问一下(轮询),也可扫码微信接受通知(回调)
以AJAX为例,request.send()之后,并不能直接得到response,必须等到readyState变为4,浏览器回头调用request.onreadystatechange函数,才能得到request.response
五、回调
回调:写给自己用的函数,并不是回调,写给别人调用的函数,才叫回调
request.onreadystatechange就是写给浏览器调用的
function f1() {}
function f2(fn){
fn()
}
f2(f1)
- 没有调用
f1 - 把
f1传给f2 f2调用了f1f1是回调
六、异步任务和回调
6.1关联
- 异步任务需要在得到结果的时候通知
JS来拿结果 - 可以让
JS写一个函数地址(电话号码)给浏览器 - 异步任务完成时浏览器调用该函数地址(拨打电话)
- 同时把结果作为参数传给该函数,让
JS拿到结果(电话里说可以回来吃了) - 这个函数是写给浏览器调用的,所以是回调函数
6.2区别
-
异步任务需要用到回调函数来通知结果,但也可以用轮询
-
回调函数不一定只用在异步任务,也可以用在同步任务
array.forEach(n => console.log(n)) // 同步回调
七、异步函数
一般来说如果一个函数的返回值处于下列三个API里面,则这个函数为异步函数(还有其他API之后再说明)
setTimeoutAJAX(即XMLHttpRequest)AddEventListener
AJAX一般不设置为同步,这样做会使得请求期间页面卡住
7.1异步函数举例
-
function 摇骰子(){ setTimeout(()=>{//箭头函数 return parseInt((Math.random()*6)+1 },1000) } const n = 摇骰子() console.log(n) //undefined- 摇骰子() 没有写
return,那么return undefined - 箭头函数里面有
return,返回真正结果 - 这是一个异步函数/异步任务
- 摇骰子() 没有写
怎么拿到异步结果?
- 可以写一个函数,把函数地址给
摇骰子()
function f1(x){console.log(x)}
摇骰子(f1)
- 然后要求摇骰子函数得到结果后把结果作为参数传给
f1
function f1(x){console.log(x)}
摇骰子(f1)
function 摇骰子(fn){
setTimeout(()=>{//箭头函数
fn(parseInt((Math.random()*6)+1) //不写return
},1000)
}
- 因为
f1声明之后只用了一次,所以可以删掉f1
function f1(x){console.log(x)}
摇骰子(f1)
//改为
摇骰子(x => {console.log(x)})
//再简化为,但是如果参数个数不一致,不能这样简化
摇骰子(console.log)
面试题:
//对回调函数错误的简化
const array = ['1','2','3'].map(parseInt)
console.log(array)
// [1,NaN,NaN]
//上述写法相当于下面
const array = ['1','2','3'].map((item,i,arr)=>{ //map会传3个参数给parseInt
return parseInt(item,i,arr)
//parseInt("1",0,arr) => 1
//parseInt("2",1,arr) => NaN 相当于把2作为一进制的数进行解析
//parseInt("3",2,arr) => NaN 相当于把3作为二进制的数进行解析
})
//正常来说要写成箭头函数
const array = ['1','2','3'].map(item => parseInt(item))
console.log(array)
7.2异步任务不同结果处理
7.2.1两种处理方式
当异步任务拿到2个结果(成功或者失败)时,如何处理?
- 一个回调接受两个参数
error,data
fs.readFile('./1.txt',(error,data)=>{
if(error){console.log('失败');return}
console.log(data.toString()) //成功
})
- 定义两个回调,一个是成功的回调,一个是失败的回调
ajax('get','1.json',data=>{},error=>{})
//前面函数是成功的回调,后面函数是失败的回调
7.2.2上述两种方法的不足之处(记忆)
- 不规范,命名五花八门,有人用success+error,有人用success+fail,还有用done+fail
- 容易出现回调地狱,代码难以理解
getUser( user => {
getGroups(user, (groups)=>{
groups.forEach( (g)=>{
g.filter(x => x.ownerId === user.id)
.forEach(x => console.log(x))
})
})
})
//上述只是四层回调,二十层回调更难理解
- 很难进行错误处理
八、Promise的用法
以AJAX封装为例
ajax = (method, url, options)=>{
const {success, fail} = options //析构赋值
const request = new XMLHttpRequest()
request.open(method, url)
request.onreadystatechange = ()=>{
if(request.readyState === 4){
// 成功调用success 失败调用fail
if(request.status < 400){
success.call(null, request.response)
}else if(request.status >= 400){
fail.call(null, request, request.status)
}
}
}
request.send()
}
ajax('get','/xxx',{
success(response){},fail:(request,status)=>{} //左边是function的缩写,右边是箭头函数
})
接下来用promise改造ajax的源代码
ajax = (method, url, options)=>{
return new Promise((resolve,reject)=>{ //返回一个promise对象
const {success, fail} = options //析构赋值
const request = new XMLHttpRequest()
request.open(method, url)
request.onreadystatechange = ()=>{
if(request.readyState === 4){
// 成功调用resolve 失败调用reject
if(request.status < 400){
resolve.call(null, request.response)
}else if(request.status >= 400){
reject.call(null, request)
}
}
}
request.send()
})
}
ajax('get','/xxx')
.then((response){},(request)=>{})
//then的第一个参数是success,第二个参数是fail
//ajax()返回一个含有.then()方法的对象
用promise封装的AJAX的缺点
post无法上传数据- 不能设置请求头
如何解决:
- 使用
jQuery.ajax - 使用
axios