前后端交互模式
1.1 接口调用方式:
- 原生ajax
- 基于jquery的ajax
- fetch
- axios
1.2 URL地址格式
我们先看一个例子:
index.js:
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
app.use(express.static('public'))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended:false}))
// 设置允许跨域访问该服务
app.all('*',function(req,res,next){
res.header("Access-Control-Allow-Origin","*");
res.header("Access-Control-Allow-Methods","PUT,GET,POST,DELETE,OPTIONS");
res.header("Access-Control-Allow-Headers","X-Requestd-With");
res.header("Access-Control-Allow-Headers","Content-Type");
next()
});
app.get('/data',(req,res)=>{
console.log('xxx');
res.send('hello world!')
})
app.listen(9999,()=>{
console.log('server running')
})
test.html:
<body>
<div>前后端交互</div>
<script src="./jquery-3.3.1.js"></script>
<script>
$.ajax({
url: 'http://localhost:9999/data',
success: function (data) {
console.log(data)
}
})
</script>
</body>
结果:
但是ajax这样的方式有一个异步的问题:
控制台输出的a还是something,我们拿不到ajax返回的值,因为ajax是异步的:
异步一个问题,还有一个问题,我们修改一下服务端代码,增加两个路由:
app.get('/data',(req,res)=>{
res.send('hello world!')
})
// 这是增加的/data1
app.get('/data1',(req,res)=>{
res.send('hello dean!')
})
// 这是增加的/data2
app.get('/data2',(req,res)=>{
res.send('hello amanda!')
})
在页面逻辑里我们再发起针对新路由的两个请求:
结果:
结果顺序是不一定固定的,要解决顺序问题就需要不断的嵌套,这样就形成了回调地狱
解决方法:我们需要Promise
下面我们看一个基于promise发送ajax请求的例子:
// 基于Promise发送ajax请求
function queryData(url) {
let p = new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState != 4) return
if (xhr.readyState === 4 && xhr.status === 200) {
// 处理正常的情况
resolve(xhr.responseText)
} else {
// 处理异常情况
reject('error')
}
}
xhr.open('get', url)
xhr.send(null)
})
return p
}
queryData('http://localhost:9999/data')
.then((data) => {
console.log('data:', data)
}, (err) => {
console.log('err:', err)
})
插播一条和onreadystatechange 事件相关的知识:
当请求被发送到服务器时,我们需要执行一些基于响应的任务。
每当 readyState 改变时,就会触发 onreadystatechange 事件。
readyState 属性存有 XMLHttpRequest 的状态信息。
下面是 XMLHttpRequest 对象的三个重要的属性:
如果要发送多个请求并且保证顺序,使用promise就可以链式调用,避免了回调地狱问题:
queryData('http://localhost:9999/data')
.then((data) => {
console.log('data0:', data)
return queryData('http://localhost:9999/data1')
})
.then((data) => {
console.log('data1:', data)
return queryData('http://localhost:9999/data2')
})
.then((data) => {
console.log('data2:', data)
})
结果:
function foo() {
return new Promise((resolve, reject) => {
setTimeout(()=>{
resolve(999)
},1000)
})
}
foo()
.then((data)=>{
console.log(data)
})
.catch((data)=>{
console.log(data)
})
.finally(()=>{
console.log('done.')
})
结果:
一秒以后显示
catch方法和可以省略,下面代码和上面代码等效:
function foo() {
return new Promise((resolve, reject) => {
setTimeout(()=>{
reject('error')
},1000)
})
}
foo()
.then((data)=>{
console.log(data)
// then方法的第二个参数可以等效为catch方法
},(data)=>{
console.log(data)
})
.finally(()=>{
console.log('done.')
})
结果:
先看下promise.all()方法:
服务端:
function queryAjax(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState !== 4) return
if (xhr.readyState === 4 && xhr.status === 200) {
resolve(xhr.responseText)
} else {
reject('error')
}
}
xhr.open('get',url)
xhr.send(null)
})
}
let p1 = queryAjax('http://localhost:9999/a1')
let p2 = queryAjax('http://localhost:9999/a2')
let p3 = queryAjax('http://localhost:9999/a3')
Promise.all([p1,p2,p3]).then((result)=>{
console.log(result);
})
3秒以后,控制台返回了一个数组:
再看看promise.race方法,只需要把all改成race就可以了:
结果:
所以race返回的是执行最快的那个异步任务得到的数据