js处理异步的几种方式
同步、异步
1.造成原因:js是单线程的语言
2.概念:
- 同步任务:实时处理 在主线程上排队执行的任务,按照先后顺序执行
- 异步任务:分时处理 不进入主线程、而进入"任务队列"的任务,只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行
- 异步模式可以一起执行多个任务
3.js处理异步的几种方式
1. 底层原理
XML
的英文全称是 EXtensible Markup Language
,即可扩展标记语言。
1.1 使用xhr
发起GET请求
// 1. 创建 XHR 对象
var xhr = new XMLHttpRequest()
// 2. 调用 open 函数
xhr.open('GET', 'http://www.shanshan.top:3006/api/getlist?id=1&list=统计') //?id=1查询字符串
// 3. 调用 send 函数
xhr.send()
// 4. 监听 onreadystatechange 事件
xhr.onreadystatechange = function () {
当 readyState 等于 4 且状态为 200 时,表示响应已就绪:
if (xhr.readyState === 4 && xhr.status === 200) {
// 获取服务器响应的数据
console.log(xhr.responseText)
}
}
1.2 使用xhr
发起POST
请求
// 1. 创建 xhr 对象
var xhr = new XMLHttpRequest()
// 2. 调用 open 函数
xhr.open('POST', 'http://www.shanshan.top:3006/api/addlist')
// 3. 设置 Content-Type 属性(固定写法)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// 4. 调用 send 函数
xhr.send('bookname=三毛&author=张三&publisher=本地图书出版社')
// 5. 监听事件
//onreadystatechange 存储函数,每当 readyState 属性改变时,就会调用该函数
xhr.onreadystatechange = function () {
// 对象的 readyState 属性,用来表示当前 Ajax 请求所处的状态
// 当 readyState 等于 4 且状态为 200 时,表示响应已就绪:
if (xhr.readyState === 4 && xhr.status === 200) {
// 获取服务器响应的数据
console.log(xhr.responseText)
}
}
1.3 FormData
对象管理表单数据
Ajax 操作往往用来提交表单数据。为了方便表单处理,HTML5
新增了一个 FormData
对象,可以模拟表单操作:
// 1. 新建 FormData 对象
var fd = new FormData()
// 2. 为 FormData 添加表单项
fd.append('uname', 'zs')
fd.append('upwd', '123456')
// 3. 创建 XHR 对象
var xhr = new XMLHttpRequest()
// 4. 指定请求类型与URL地址
xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata')
// 5. 直接提交 FormData 对象,这与提交网页表单的效果,完全一样
xhr.send(fd)
FormData
对象也可以用来获取网页表单的值,示例代码如下:
// 获取表单元素
var form = document.querySelector('#form1')
// 监听表单元素的 submit 事件
form.addEventListener('submit', function(e) {
e.preventDefault()
// 根据 form 表单创建 FormData 对象,会自动将表单数据填充到 FormData 对象中
var fd = new FormData(form)
var xhr = new XMLHttpRequest()
xhr.open('POST', 'http://www.liulongbin.top:3006/api/formdata')
xhr.send(fd)
xhr.onreadystatechange = function() {}
})
2. 原生ajax
2.1 jQuery中的Ajax
$.ajax({
type:'GET', //请求方式
url:'http://www.shanshan.top:3006/api/getlist', //请求的url地址
data:{id:1}, //这次请求需携带的参数
success:function(res){ //请求成功之后的回调函数
console.log(res) //res 后台返回的数据
}
})
$.ajax({
type:'POST',
url:'http://www.shanshan.top:3006/api/addlist',
data:{
name:'张三',
sex:'男'
},
success:function(res){
console.log(res)
}
})
2.2 通过Ajax提交表单数据
<from id="from1">
<input type="text" name="username">
<input type="password" name="password">
<button type="submit">提交</button>
</from>
<script>
$('#from1').submit(function (e) {
e.preventDefault() //阻止表单的默认提交和页面的跳转
var data = $(this).serialize() //一次性获取表单的数据,必须为每个表单元素添加 name 属性
})
</script>
多次调用ajax时,不是按照书写ajax代码的顺序返回结果,如果存在依赖关系就需要嵌套,会造成回调地狱
$.ajax({
url: 'http://localhost:3000/data',
success: function(data) {
console.log(data)
$.ajax({
url: 'http://localhost:3000/data1',
success: function(data) {
console.log(data)
$.ajax({
url: 'http://localhost:3000/data2',
success: function(data) {
console.log(data)
}
});
}
});
}
});
3. promise对象主要解决异步深层嵌套造成回调地狱的问题--es6
promise对象是一个构造函数,用来生成Promise实例
3.1 基本用法
function timeout() {
var p = new Promise(function(resolve,reject){
//异步操作
setTimeout(function(){
var flag = false;
if(flag) {
//3.失败时调用resolve()
resolve('hello');
}else{
//4.成功是调用reject()
reject('出错了');
}
}, 100);
});
return p
}
//p.then获取处理结果
p.then(function(ret){
从resolve得到的正常结果
},function(ret){
从reject得到的错误信息
})
3.2 基于Promise发送Ajax请求
<script type="text/javascript">
/*
基于Promise发送Ajax请求
*/
function queryData(url) {
# 1.1 创建一个Promise实例
var p = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
# 1.2 处理正常的情况
resolve(xhr.responseText);
}else{
# 1.3 处理异常情况
reject('服务器错误');
}
};
xhr.open('get', url);
xhr.send(null);
});
return p;
}
#发送多个ajax请求并且保证顺序
多个ajax任务通过 .then 的方式变成了线性关系,保证的执行的顺序
queryData('http://localhost:3000/data')
.then(function(data){
console.log(data)
//return的是一个新的Promise对象,下一个then的调用者就是上一个return出来的Promise对象
return queryData('http://localhost:3000/data1');
})
.then(function(data){ //data接收上一个异步任务返回的结果
console.log(data);
return queryData('http://localhost:3000/data2');
})
.then(function(data){ //p.then()得到异步任务的正确结果 resolve中的信息
console.log(data)
})
.catch(function(data){ //p.catch()获取异常信息 reject中的信息
console.log(data)
})
.finally(function(){ //p.finally()成功与否都会执行(不是正式标准
console.log("成功与否都会执行")
})
</script>
- 在then方法中函数的返回值,你也可以直接return数据而不是Promise对象,在后面的then中就可以接收到数据了
3.3 Promise常用的API
1.实例方法
- p.then ( ) 得到异步任务的正确结果 resolve中的信息
- p.catch ( ) 获取异常信息 reject中的信息
- p.finally ( ) 成功与否都会执行(不是正式标准)
2.对象方法
.all()
-
Promise.all
方法接受一个数组作参数,数组中的对象(p1、p2、p3)均为promise实例(如果不是一个promise,该项会被用Promise.resolve
转换为一个promise)。它的状态由这三个promise实例决定 -
并发处理多个异步任务,所有任务都执行完成才能得到结果
.race()
-
Promise.race
方法同样接受一个数组作参数。当p1, p2, p3中有一个实例的状态发生改变(变为fulfilled
或rejected
),p的状态就跟着改变。并把第一个改变状态的promise的返回值,传给p的回调函数 -
并发处理多个异步任务,只要有一个任务完成就能得到结果
function queryData(url) {
# 1.1 创建一个Promise实例
var p = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
# 1.2 处理正常的情况
resolve(xhr.responseText);
}else{
# 1.3 处理异常情况
reject('服务器错误');
}
};
xhr.open('get', url);
xhr.send(null);
});
return p;
}
/*
Promise常用API-对象方法
*/
var p1 = queryData('http://localhost:3000/a1');
var p2 = queryData('http://localhost:3000/a2');
var p3 = queryData('http://localhost:3000/a3');
Promise.all([p1,p2,p3]).then(function(result){
//all 中的参数 [p1,p2,p3] 和 返回的结果一 一对‘应["TOM", "JERRY", "SPIKE"]
console.log(result) //["TOM", "JERRY", "SPIKE"]
})
Promise.race([p1,p2,p3]).then(function(result){
// 由于p1执行较快,Promise的then()将获得结果'P1'。 p2,p3仍在继续执行,但执行结果将被丢弃。
console.log(result) // "TOM"
})
4.接口调用---fetch --- xhr的升级版
fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。
4.1 基本用法
<script type="text/javascript">
/*
Fetch API 基本用法 fetch(url).then()
第一个参数请求的路径 Fetch会返回Promise 所以我们可以使用then 拿到请求成功的结果
*/
fetch('http://localhost:3000/fdata').then(function(data){
// text()方法属于fetchAPI的一部分,它返回一个Promise实例对象,用于获取后台返回的数据
return data.text();
}).then(function(data){
// 在这个then里面我们能拿到最终的数据
console.log(data);
})
</script>
4.2 fetch API 中的 HTTP 请求
1 GET参数传递 - 传统URL 通过url ? 的形式传参
fetch('http://localhost:3000/books?id=123', {
# get 请求可以省略不写 默认的是GET
method: 'get'
})
.then(function(data) {
# 它返回一个Promise实例对象,用于获取后台返回的数据
return data.text();
}).then(function(data) {
# 在这个then里面我们能拿到最终的数据
console.log(data)
});
2 GET参数传递 restful形式的URL 通过/ 的形式传递参数 即 id = 456 和id后台的配置有关
fetch('http://localhost:3000/books/456', {
# get 请求可以省略不写 默认的是GET
method: 'get'
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
3 DELETE请求方式参数传递 删除id 是 id=789
fetch('http://localhost:3000/books/456', {
# get 请求可以省略不写 默认的是GET
method: 'get'
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
4 POST请求传参
fetch('http://localhost:3000/books', {
method: 'post',
# 3.1 传递数据
body: 'uname=lisi&pwd=123',
# 3.2 设置请求头
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
fetch('http://localhost:3000/books', {
method: 'post',
body: JSON.stringify({
uname: '张三',
pwd: '456'
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
5 PUT请求传参 修改id 是 123 的
fetch('http://localhost:3000/books/123', {
method: 'put',
body: JSON.stringify({
uname: '张三',
pwd: '789'
}),
headers: {
'Content-Type': 'application/json'
}
})
.then(function(data) {
return data.text();
}).then(function(data) {
console.log(data)
});
fetchAPI 中 响应格式
fetch('http://localhost:3000/json').then(function(data){
// return data.json(); // 将获取到的数据使用 json 转换对象
return data.text(); // // 将获取到的数据 转换成字符串
}).then(function(data){
// console.log(data.uname)
// console.log(typeof data) string
var obj = JSON.parse(data); //转化成对象
console.log(obj.uname,obj.age,obj.gender)
})
5.接口调用--axios
Axios 是一个基于 promise 的 HTTP 库,是专注于网络数据请求的库,可以用在浏览器和 node.js 中。
5.1 综合写法
axios({
method:"请求的方式"
url:"请求的地址"
data:发送的数据
headers:请求头
}).then(()=>{})
.catch(()=>{})
5.2 axios 全局配置
# 配置公共的请求头
axios.defaults.baseURL = 'https://api.example.com';
# 配置 超时时间
axios.defaults.timeout = 2500;
# 配置公共的请求头
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
# 配置公共的 post 的 Content-Type
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
5.3 axios 拦截器
5.3.1 请求拦截器
-
axios.interceptors.request.use(function(config) { console.log(config.url) # 1.1 任何请求都会经过这一步 在发送请求之前做些什么 config.headers.mytoken = 'nihao'; # 1.2 这里一定要return 否则配置不成功 return config; }, function(err){ #1.3 对请求错误做点什么 console.log(err) })
5.3.2 响应拦截器
-
axios.interceptors.response.use(function(res) { #2.1 在接收响应做些什么 var data = res.data; return data; }, function(err){ #2.2 对响应错误做点什么 console.log(err) })
5.4 创建axios实例
-
const instance = axios.create({ baseURL: 'https://some-domain.com/api/', timeout: 1000, headers: {'X-Custom-Header': 'foobar'} });
6. **async await **让异步代码看起来更像同步代码 --ES7
async关键字 放到函数前面 (返回值是一个promise
实例对象)
await关键字 只能在使用async
定义的函数中使用(得到异步的结果,await只能得到成功的结果,如果想要得到失败的结果,只能用trycatch)
6.1处理单个异步请求
async getFaceResult () {
//用的是try/catch来捕获异常,把await放到try中进行执行
//如有异常,就使用catch进行处理,不会影响后面的代码
try {
let location = await getLocation()
} catch(err) {
console.log("错误",err)
}
}
6.2处理多个异步请求
async getFaceResult () {
try {
// 添加await之后 当前的await 返回结果之后才会执行后面的代码
let location = await getLocation()
let ret = await getList()
} catch(err) {
console.log("错误",err)
}
}