Ajax
1. 介绍
AJAX,是 Asynchronous JavaScript And XML,意思是利用JavaScript执行异步网络请求,对页面进行局部更新,而不需要重载页面。
AJAX是浏览器的功能,利用浏览器在window上增加的XMLHttpRequest函数,利用该函数构造出一个对象,使用该对象进行请求发送与响应接受。
2. 使用
- 创建一个
XMLHttpRequest对象 - 调用对象的
open方法启用。 - 监听对象的
onreadystatechange事件(或者是onload和onerror事件),处理函数对返回的数据进行处理。 - 调用对象的
send方法发送请求。 完整版:
var request = new XMLHttpRequest()
request.open('GET','/xxx')
request.onreadystateChange = function(
if(request.readyState === 4){
console.log('请求完成')
if(request.responese.status >= 200 && request.response.status < 300){
console.log('请求成功')
}else{
console.log('请求失败')
}
}
)
request.send()
3. onreadyState状态值
创建 -> 打开 -> 发送 -> 接收 -> 完成
每一次state的改变都会触发readystatechange事件,但我们一般只关心state为4的阶段,此阶段数据接收已完成,可来进行数据处理。onload函数也会在请求完成后即state为4的时候调用。
终止请求
使用XHR对象的abort方法会终止该请求,state会被重置为0。
Promise
异步与回调
- 回调函数: 当一个函数作为参数传入另一个参数中,并且它不会立即执行,只有当满足一定条件后该函数才可以执行,这种函数就称为回调函数。我们熟悉的定时器和Ajax中就存在有回调函数:
setTimeout(function(){
console.log('执行了回调函数');
},3000)
这里的function(){console.log('执行了回调函数')}就是回调函数,它只有在3秒后才会执行
/1.创建异步对象
var xhr=new XMLHttpRequest();
//2.绑定监听事件(接收请求)
xhr.onreadystatechange=function(){
//此方法会被调用4次
//最后一次,readyState==4
//并且响应状态码为200时,才是我们要的响应结果 xhr.status==200
if(xhr.readyState==4 && xhr.status==200){
//把响应数据存储到变量result中
var result=xhr.responseText;
console.log(result);
}
}
//3.打开链接(创建请求)
xhr.open("get","/demo/ajaxDemo",true);
//4.发送请求
xhr.send();
这里的回调函数是xhr.onreadystatechange绑定的函数,在xhr.send()发送请求并拿到响应后执行。
- 异步任务 与之相对应的概念是“同步任务”,同步任务在主线程上排队执行,只有前一个任务执行完毕,才能执行下一个任务。异步任务不进入主线程,而是进入异步队列,前一个任务是否执行完毕不影响下一个任务的执行。同样,还拿定时器作为异步任务举例:
setTimeout(function(){
console.log('执行了回调函数');
},3000)
console.log('111')
结果输出为先111,后“执行了回调函数”。
这种不阻塞后面任务执行的任务就叫做异步任务。
回调地狱
要使用异步输出“武林要以和为贵,要讲武德,不要搞窝里斗”
setTimeout(function () { //第一层
console.log('武林要以和为贵');
setTimeout(function () { //第二程
console.log('要讲武德');
setTimeout(function () { //第三层
console.log('不要搞窝里斗');
}, 1000)
}, 2000)
}, 3000)
可以看到,代码中的回调函数套回调函数,居然套了3层,这种回调函数中嵌套回调函数的情况就叫做回调地狱。
总结一下,回调地狱就是为是实现代码顺序执行而出现的一种操作,它会造成我们的代码可读性非常差,后期不好维护。
那该如何解决回调地狱呢?下面我们介绍Promise
Promise
promise特点
-
promise是ES6用来解决异步问题,将异步操作以同步流程表达出来,避免了回调地狱的一个对象。它可以解决异步问题,但不能说promise是异步的。
-
一个 Promise 必然处于以下几种状态之一:
- 待定(pending) :初始状态,既没有被兑现,也没有被拒绝。
- 已成功(resolved) :意味着操作成功完成。
- 已拒绝(rejected) :意味着操作失败。 一旦状态改变就不会再变,任何时候都是这个结果。状态改变只能是:pending --> resolved / rejected
-
promise内部发生错误,不会影响到外部程序的执行。
-
无法取消Promise。一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
基础用法
创建promise实例时,必须传入一个函数作为参数
new Promise(() => {});
new Promise();
该函数可以接受另外两个由js引擎提供的函数,resolve和reject。
resolve:将Promise对象的状态从pending变为resolved,将异步操作的结果作为参数传出去
reject:将Promise对象状态从pending变为rejected,将异步操作报出的错误作为参数传出去
let promise = new Promise((resolve,reject) => {
// do something
if(true){
// 将参数返回,供.then方法调用
resolve("value");
} else {
reject("error");
}
});
promise实例生成后,可用then方法分别指定resolved状态和rejected状态的回调函数
promise.then(
value => {
//resolved时调用,value为resolved函数时返回的参数
console.log(value);
},
err => {
// rejectd时调用,err为reject函数返回的参数
console.log(error);
}
);
当then方法只有一个函数参数时,为resolved状态的回调方法
promise.then(value => {
// 只有状态为resolved才能调用,如果返回的是rejected状态,则报错Uncaught(in promise)error
console.log(value);
});
只有当promise状态变为resolved或者rejected时才会调用then方法
Promise新建后就会立即执行,并且调用resolve或reject参数的执行。
let promise = new Promise(function(resolve) {
console.log("Promise");
resolve(); // resolve调用以后才会执行then方法
console.log("!!!");
});
promise.then(function() {
console.log("resoled");
});
console.log("Hi");
resolve返回的是另一个promise实例
const p1 = new Promise((_, reject) => {
setTimeout(() => reject('error'), 3000));
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve(p1), 1000);
});
p2.then(
result => console.log(result),
error => console.log(error)
);
上面的代码中,p1是一个promise,3s变为rejected。p2状态在1s后变为resolved,返回p1。由于p2返回的是另一个promise,导致p2自己的状态失效了,由p1的状态决定p2的状态。所以后面的then语句都变成针对后者p1。也就是说p2在变成resolved以后2s又变成了rejected,导致触发catch方法指定的回调函数。
使用案例:
const task = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('成功');
}, 3000);
})
task.then((res) => {
// 3秒后打印成功 console.log(res);
})
// 看这个例子似乎没什么特别之处,感觉就是把callback换成resolve, 那么再看看下面一个
// 获取列表之前,需要先判断是否登录,then的链式调用
new Promise((resolve, reject) => {
$.ajax('/api/user/login', {
success: function(isLogin) {
if (isLogin) {
resolve(data);
} else {
reject('没有登录');
}
}
})
}).then(
res => {
$.ajax('/api/list', {
success: function(data) {
resolve(data);
}
})
},
reason => {
alert(reason)
}
).then((data) => {
// data为list数据 // 在这里还可以为list排序 return data.sort();
}).then((list) => {
// 好了,这里的拿到list 可以使用了
})