(一) 前后的交互模式
- 之前处理的案例都是静态数据,而真正的项目都是需要后台提供真正的数据的
- 那么前台就需要通过后台提供的数据接口获取数据
1. 接口调用方式
- 原生
ajax
:比较麻烦,一般不用 - 基于
jQuery
的ajax
:这个会简单点,但jquery
侧重DOM操作,而Vue很少涉及DOM操作 fetch
:ajax升级版,标准化组织制定的一套新规范axios
:第三方的库,比fetch更加强大
客户端与服务器通信方式
- 左侧为客户端,右侧为服务器
- 客户端通过互联网向服务器发起请求(寻求页面,数据)
- 服务器通过互联网向客户端做出响应(返回页面,数据)
2. Url
地址格式
url
:Uniform Resource Locator
,统一资源定位符,我们理解为地址即可- 通过
url
可以获取对应的资源或数据
2.1 传统形式的URL
格式:schema://host:port/path?query#fragment
schema
:协议。例如http、https、ftp等host
:域名或者IP地址port
:端口,http默认端口80,可以省略path
:路径,例如/abc/a/b/cquery
:查询参数,例如uname=lisi&age=12fragment
:锚点(哈希Hash),用于定位页面的某个位置
符合规则的URL
- http://www.itcast.cn
- http://www.itcast.cn/java/web
- http://www.itcast.cn/java/web?flag=1
- http://www.itcast.cn/java/web?flag=1#function
2.2Restful
形式的url
Restful
是定义url
的一种规则。目前公司内部使用Restful
设计风格的url
比较多
Restful
形式的url
,如果要通过url
传递参数,不是?
拼接,而是直接以 /
拼接即可
HTTP请求方式
GET
查询POST
添加PUT
修改DELETE
删除
符合规则的URL地址
- http://www.hello.com/books GET
- http://www.hello.com/books POST
- http://www.hello.com/books/123 PUT
- http://www.hello.com/books/123 DELETE
根据请求方式,来确定具体操作是增删改查哪种
第一个url和第二个url是一样的,但是请求方式不一样
get
代表获取图书列表post
代表新增图书数据,并且会通过请求体传递新增图书的数据
第三个url代表要更新id为123的图书信息,更新的图书数据通过请求体传递
(二) Promise
用法
1. 异步调用
- 如果是要发起请求,访问接口获取数据,因为这个过程是比较耗时的,所以需要异步
1.1 异步效果分析(以下均是异步)
定时任务
setTimeout(function(){
},10000)
事件函数
xxx.addEventListener('click',function(){
})
xxx.onclick=function(){
} // 即使这里的function不是回调函数,但也是异步
只要是回调函数就是异步:(函数作为参数传递的都是回调函数)
Ajax
<body>
<div>前后端交互</div>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript">
/*
前后端交互-异步编程与Promise概述
*/
var ret = '---';
$.ajax({
url: 'http://localhost:3000/data',//接口返回HelloWorld
success: function(data) {
ret = data;
console.log(ret)//这里可以拿到HelloWorld
}
});
console.log(ret)//这里是没有办法拿到HelloWorld,因为ajax发起请求是异步的
</script>
<body>
在/myapi 文件夹中 的index.js 运行node
先输入 npm i
再输入 nodemon index.js
- index.js中有/data路由,并且返回Hello World!数据
- 服务运行在3000端口
- 终端进入myapi目录之后,运行nodemon index.js
- nodemon可以检测代码修改,自动重启服务
1.2 多次异步调用的依赖分析
多次异步调用的结果顺序不确定
第一步:发起三次请求,观察结果:
$.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)
}
});
第二步: 修改后台
在在/myapi 文件夹中 的index.js 添加data1和data2路由
// 路由
app.get('/data', (req, res) => {
res.send('Hello World!')
})
app.get('/data1', (req, res) => {
setTimeout(function() {
res.send('Hello TOM!')
}, 1000);
})
app.get('/data2', (req, res) => {
res.send('Hello JERRY!')
})
第三步:在浏览器中打开——效果
这个打印的顺序不不固定的,而是根据网络状态而定
虽然这次看到的结果,跟咱们发起请求的代码顺序一致,但是下次就可能不一定了
异步调用结果如果存在依赖需要嵌套
如果我们需要在data请求成功之后再请求data1,然后需要在data1请求成功之后再请求data2,代码如下:
$.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)
}
});
}
});
}
});
完整代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div>前后端交互</div>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript">
//前后端交互-异步编程与Promise概述
var ret = '---';
$.ajax({
url: 'http://localhost:3000/data',
success: function(data) {
ret = data;
console.log(ret)
}
});
console.log(ret)
// ----------------------------
// $.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)
// }
// });
// -----------------------------------
$.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)
}
});
}
});
}
});
</script>
</body>
</html>
上述代码的层级太深,可读性比较糟糕,可以用Promise
来解决
2. Promise
概述
Promise
是异步编程的一种解决方案,从语法上讲,Promise
是一个对象,从它可以获取异步操作的消息。
使用Promise
主要有以下好处:
- 可以避免多层异步调用嵌套问题(回调地狱)
Promise
对象提供了简洁的API,使得控制异步操作更加容易
网址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
Promise
总结:就是处理异步逻辑的一个工具
3. Promise
基本用法
- 实例化
Promise
对象,构造函数中传递函数,该函数中用于处理异步任务 resolve
和reject
两个参数用于处理成功和失败两种情况,并通过p.then
获取处理结果
var p = new Promise(function(resolve , reject) {
// 成功时调用 resolve()
// 失败时调用 reject()
});
p.then(function(ret) {
// 从 resolve 得到正常结果
} , function(ret) {
// 从 reject 得到错误信息
});
resolve
:解决,理解:解决了,就是成功了
reject
:拒绝,理解:被决绝了,就是失败了
then
:请求结束之后,不管成功失败都会调用then
方法
- 它最多需要有两个参数:
Promise
的成功和失败情况的回调函数 then
:然后,接下来。理解:请求结束,接下来处理结果
示例代码:
我们先让Promise处理一下setTimeout这种异步逻辑:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
/*
Promise基本使用
*/
console.log(typeof Promise)
console.dir(Promise);
var p = new Promise(function (resolve, reject) {
// 这里用于实现异步任务
setTimeout(function () {
var flag = false;
if (flag) {
// 正常情况
resolve('hello');
} else {
// 异常情况
reject('出错了');
}
}, 100);
});
p.then(function (data) {
//data接收resolve传递过来的hello
console.log(data) //如果flag为true,输出hello
}, function (info) {
//info接收reject传递过来的出错了
console.log(info) //如果flag为false,输出出错了
});
</script>
</body>
</html>
4. 基于Promise
处理Ajax
请求并处理回调地狱问题
4.1 处理原生Ajax
XMLHttpRequest
是Ajax
的核心,Ajax
内部就是通过XMLHttpRequest
来发起请求的
// Promise如何处理XMLHttpRequest这个异步逻辑
function queryData(){
return new Promise(function(resolve , reject) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return;
if(xhr.readyState == 4 && xhr.status == 200) {
resolve(xhr.responseText)
} else {
reject('出错了');
}
}
xhr.open('get' , '/data');
xhr.send(null);
})
}
queryData('http://localhost:3000/data')
.then(function (data) {
console.log(data);
}, function (info) {
console.log(info);
});
onreadystatechange
是捕获请求的状态码,只要状态码发生变化就会调用- 我们不关心状态不为4的,所以如果
readyState
不等于4直接返回 4
是请求操作已经完成,意思就是请求完成之后,在执行以下逻辑status
:是响应状态码,如果为200说明成功responseText
是收到的响应数据
- 我们不关心状态不为4的,所以如果
open
初始化请求:请求方式,urlsend
:发起请求,null
代表请求体(get不需要请求体)
4.2 发送多次Ajax
请求
queryData()
.then(function(data){
return queryData();
})
.then(function(data){
return queryData();
})
.then(function(data){
return queryData();
})
queryData
返回了一个Promise
对象,所以可以调用then
方法then
方法又返回了queryData
方法的调用,相当于又返回了一个Promise
对象,所以可以接着调用then
方法
示例代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
/*
基于Promise发送Ajax请求
*/
function queryData(url) {
var p = new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState != 4) return; //请求状态不等于4,说明异常
if(xhr.readyState == 4 && xhr.status == 200) {
// 处理正常的情况
resolve(xhr.responseText);
}else{
// 处理异常情况
reject('服务器错误');
}
};
xhr.open('get', url); //初始化请求
xhr.send(null); //发送请求
});
return p;
}
// queryData('http://localhost:3000/data')
// .then(function(data){
// console.log(data);
// },function(info){
// console.log(info)
// });
// ============================
// 发送多个ajax请求并且保证顺序
queryData('http://localhost:3000/data')
.then(function(data){
//data请求结束之后调用
console.log(data)
return queryData('http://localhost:3000/data1'); //然后查询data1
})
.then(function(data){
//data1请求结束之后调用
console.log(data);
return queryData('http://localhost:3000/data2'); //最后查询data2
})
.then(function(data){
//data2请求结束之后调用
console.log(data)
});
</script>
</body>
</html>
总结:想要确保发送多个ajax
请求并且保证顺序,就需要在上一个请求结束之后的then
中发送第二个请求
5.Promise
的then
方法参数中的函数返回值
then
方法的参数是一个回调函数,而这个回调函数是有返回值的
返回值有两种情况:
- 返回Promise实例对象:返回的该实例对象会调用下一个
then
- 返回普通值:返回的普通值会直接传递给下一个
then
,通过then
参数中函数的参数接收该值
5.1 返回Promise
实例对象
返回的该实例对象会调用下一个then
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript">
/*
then参数中的函数返回值
*/
function queryData(url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState != 4) return;
if (xhr.readyState == 4 && xhr.status == 200) {
// 处理正常的情况
resolve(xhr.responseText);
} else {
// 处理异常情况
reject('服务器错误');
}
};
xhr.open('get', url);
xhr.send(null);
});
}
queryData('http://localhost:3000/data')
.then(function (data) {
return queryData('http://localhost:3000/data1')
})
.then(function (data) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(123);
}, 1000)
})
})
.then(function (data) {
console.log(data); // 123
})