前言
js执行机制
js是一个单线程的执行环境,即一次只能执行一个任务。如果有数个任务,那么这些任务会从上至下依次挨个执行。这种执行方式的好处是简单,实现也简单,缺点是这个如果有大量的任务,或者其中一个任务耗时时间很长,其他的任务暂时无法执行,就会造成浏览器无响应(俗称假死,卡死)。而前端主动去找后端要数据就是一种耗时操作,所以使用了”异步模式。
异步也叫非阻塞模式,浏览器在下载js的同时,还会执行后续的页面处理.
什么时候需要异步:
1 需要等待的情况
2 在等待过程中不能像alert一样阻塞程序运行
3 等待的情况需要异步
异步操作
例如:有三个数据请求,要在异步操作时按照下面顺序进行请求
1:'api.github.com/users/ruany…'
2:'api.github.com/users/ruany…'
3:'api.github.com/search/issu…'
回调地狱的出现
完成'api.github.com/users/ruany…
浏览器向服务器发送请求,服务器相应请求返回数据,浏览器执行异步操作。
首先进行数据请求实例话 xhr ,来做ajax这是一个耗时的任务。
let xhr = new XMLHttpRequest();
由于要实现三个数据请求,所以进行封装ajax的请求,因为要进行耗时操作,使用回调函数,
当请求的url数据到了之后,在使用callback函数对url数据进行操作。
function loadAjaxContent(url,callback) {
console.log(xhr.readyState,'-------1');
xhr.open('GET',url); // GET 动词 http 三次握手 建立好通信的通道
console.log(xhr.readyState,'-------2');
xhr.onreadystatechange = function() {
console.log(xhr.readyState,'-------4');
if(xhr.readyState ==4 && xhr.status ==200) {
callback(JSON.parse(xhr.responseText));
}
}
xhr.send();
console.log(xhr.readyState,'-------3');
}
分析上面代码:
1. 把'http://api.github.com/users/ruanyf'上的数据拿到要经过以下几个步骤:
-
xhr.open('GET',url);打开一个请求通道,传一个谓语动词“GET,以明文的方式发送一个网络请求,js自主发送请求能力。 -
xhr.send();发送请求 -
xhr.onreadystatechange{}监听,然后返回请求的数据
2. xhr.onreadystatechange是 事件监听的方式
3. if(xhr.readyState ==4 && xhr.status ==200)当xhr.readyState为4表示响应已完成,但是也可以为失败的完成(服务器中出现语法错误或404),所以加一个状态码的判断。
4. callback(xhr.responseText);由于在xhr(ie6的时代)时代没有发明json,但是json诞生后发现:
json {login:'ruanyf'} 作为数据传输的格式,
比xml 更轻量 <userInfo><login>ruanyf</login><userInfo>
所以将callback(xhr.responseText)改为callback(JSON.parse(xhr.responseText));
5. 调用函数loadAjaxContent()的执行结果:
loadAjaxContent('http://api.github.com/users/ruanyf',(users) => {
console.log(users,'users');
});
xhr.readyState 有哪几个值
-
0 -(未初始化)还没有调用send()方法
-
1 -(载入)已调用send()方法,正在发送请求
-
2 - 请求已发送, 正在处理中 pending 服务器正在做一些运算
-
3 - 请求在处理中, 已有部分数据, 将大的数据包进行分片切割 断点续传
-
4 -(完成)响应内容解析完成,可以获取并使用服务器的响应了
回调地狱的出现----完成三个数据请求的顺序执行
loadAjaxContent('http://api.github.com/users/ruanyf',(users) => {
console.log(users,'users');
loadAjaxContent('http://api.github.com/users/ruanyf/repos',(repos) => {
console.log(repos,'repos') ;
loadAjaxContent('http://api.github.com/search/issues?q=ruanyf',(results) => {
console.log(results,'results') ;
});
});
});
执行结果:
成功顺序的执行了数据请求。但是如果有10个数据请求的话,会变成
回调地狱
回调地狱的解决
使用Promise
Promise 函数是立即执行的,Promise是包一些ajax这样的耗时任务,当它执行完之后, 其中resolve,会继续执行下去。resolve reject 就是 交出执行的控制权。当使用Promise
<script>
let xhr = new XMLHttpRequest();
let p1 = new Promise((resolve,reject) => {
console.log('bbbb'); // Promise 函数是立即执行的
xhr.open('GET','http://api.github.com/users/ruanyf');
xhr.onreadystatechange = function() {
if(xhr.readyState ==4 && xhr.status ==200) {
resolve(JSON.parse(xhr.responseText));
}
}
xhr.send();
})
p1
.then(data => { // users
console.log('uses Promise',data);
})
</script>
先执行let p1这请求,然后做.then()中的内容。
实现了数据的输出:
使用fetch
使用fetch,"GET"请求直接fetch('api.github.com/users/ruany… 如图:
其中对应xhr的值为2(请求已发送, 正在处理中 pending 服务器正在做一些运算)。因为返回的Promise,所以可以使用.then。
let xhr = new XMLHttpRequest();
fetch('http://api.github.com/users/ruanyf')
.then(data => {
console.log('user',data)
})
直接打印data,返回的是readstream 二进制流利用data.json()转化为json对象,这也要花时间。
完成三个数据请求:
let xhr = new XMLHttpRequest();
fetch('http://api.github.com/users/ruanyf') // promise
.then(data => data.json()) //readstream 二进制流 json化 变成json对象 也是要花时间的
.then(data => {
console.log('user',data)
return fetch('http://api.github.com/users/ruanyf/repos')
.then(data => data.json())
.then(data => {
console.log('repos',data)
return fetch('http://api.github.com/search/issues?q=ruanyf')
})
.then(data => data.json())
.then(data => {
console.log('results',data);
})
})
在then回调函数中, 使用return promise实例 ,可以继续then able。
现在只有一层,看起来是同步的,不会有嵌套函数,不会再出现回调地狱。
总结
在控制多个异步任务的按顺序执行,回调函数会出现嵌套,一直嵌套一直嵌套,出现了回调地狱使用fetch内置的.then,只有一层函数。