先说一下 async 的用法,它作为一个关键字放到函数后面,用于示意函数是一个异步函数,因为 async 就是异步的意思,异步函数也就意味着该函数的执行不会阻塞前面代码的执行。写一个 async 函数
async function timeout() {return 'hello world';}
语法很简略,就是在函数后面加上 async 关键字,来示意它是异步的,那怎么调用呢?async 函数也是函数,平时咱们怎么应用函数就怎么应用它,间接加括号调用就能够了,为了示意它没有阻塞它前面代码的执行,咱们在 async 函数调用之后加一句 console.log;
async function timeout() {return 'hello world'}
timeout();
console.log('尽管在前面,然而我先执行');
关上浏览器控制台,咱们看到了
async 函数 timeout 调用了,然而没有任何输入,它不是应该返回 ‘hello world’, 先不要焦急,看一看 timeout() 执行返回了什么?把下面的 timeout() 语句改为 console.log(timeout())
async function timeout() {return 'hello world'}
console.log(timeout());
console.log('尽管在前面,然而我先执行');
持续看控制台
原来 async 函数返回的是一个 promise 对象,如果要获取到 promise 返回值,咱们应该用 then 办法,持续批改代码
async function timeout() {return 'hello world'}
timeout().then(result => {console.log(result);
})
console.log('尽管在前面,然而我先执行');
看控制台
咱们获取到了 ”hello world’, 同时 timeout 的执行也没有阻塞前面代码的执行,和咱们方才说的统一。
这时,你可能留神到控制台中的 Promise 有一个 resolved,这是 async 函数外部的实现原理。如果 async 函数中有返回一个值 , 当调用该函数时,外部会调用 Promise.solve() 办法把它转化成一个 promise 对象作为返回,但如果 timeout 函数外部抛出谬误呢?那么就会调用 Promise.reject() 返回一个 promise 对象,这时批改一下 timeout 函数
async function timeout(flag) {if (flag) {return 'hello world'} else {throw 'my god, failure'}
}
console.log(timeout(true)) // 调用 Promise.resolve() 返回 promise 对象。console.log(timeout(false)); // 调用 Promise.reject() 返回 promise 对象。
控制台如下:
如果函数外部抛出谬误,promise 对象有一个 catch 办法进行捕捉。
timeout(false).catch(err => {console.log(err)
})
async 关键字差不多了,咱们再来思考 await 关键字,await 是期待的意思,那么它期待什么呢,它前面跟着什么呢?其实它前面能够放任何表达式,不过咱们更多的是放一个返回 promise 对象的表达式。留神 await 关键字只能放到 async 函数外面
当初写一个函数,让它返回 promise 对象,该函数的作用是 2s 之后让数值乘以 2
// 2s 之后返回双倍的值
function doubleAfter2seconds(num) {return new Promise((resolve, reject) => {setTimeout(() => {resolve(2 * num)
}, 2000);
} )
}
当初再写一个 async 函数,从而能够应用 await 关键字,await 前面搁置的就是返回 promise 对象的一个表达式,所以它前面能够写上 doubleAfter2seconds 函数的调用
async function testResult() {let result = await doubleAfter2seconds(30);
console.log(result);
}
当初调用 testResult 函数
testResult();
关上控制台,2s 之后,输入了 60.
当初咱们看看代码的执行过程,调用 testResult 函数,它外面遇到了 await, await 示意等一下,代码就暂停到这里,不再向下执行了,它等什么呢?等前面的 promise 对象执行结束,而后拿到 promise resolve 的值并进行返回,返回值拿到之后,它持续向下执行。具体到 咱们的代码, 遇到 await 之后,代码就暂停执行了,期待 doubleAfter2seconds(30) 执行结束,doubleAfter2seconds(30) 返回的 promise 开始执行,2 秒 之后,promise resolve 了,并返回了值为 60,这时 await 才拿到返回值 60,而后赋值给 result,暂停完结,代码才开始继续执行,执行 console.log 语句。
就这一个函数,咱们可能看不出 async/await 的作用,如果咱们要计算 3 个数的值,而后把失去的值进行输入呢?
async function testResult() {let first = await doubleAfter2seconds(30);
let second = await doubleAfter2seconds(50);
let third = await doubleAfter2seconds(30);
console.log(first + second + third);
}
6 秒后,控制台输入 220, 咱们能够看到,写异步代码就像写同步代码一样了,再也没有回调地区了。
再写一个实在的例子,我原来做过一个小性能,话费充值,当用户输出电话号码后,先查找这个电话号码所在的省和市,而后再依据省和市,找到可能充值的面值,进行展现。
为了模仿一下后端接口,咱们新建一个 node 我的项目。新建一个文件夹 async, 而后 npm init -y 新建 package.json 文件,npm install express –save 装置后端依赖,再新建 server.js 文件作为服务端代码,public 文件夹作为动态文件的搁置地位,在 public 文件夹外面放 index.html 文件,整个目录如下
server.js 文件如下,建设最简略的 web 服务器
const express = require('express');
const app = express();// express.static 提供动态文件,就是 html, css, js 文件
app.use(express.static('public'));
app.listen(3000, () => {console.log('server start');
})
再写 index.html 文件,我在这里用了 vue 构建页面,用 axios 发送 ajax 申请,为了简略,用 cdn 引入它们。html 局部很简略,一个输入框,让用户输出手机号,一个充值金额的展现区域,js 局部,依照 vue 的要求搭建了模版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Async/await</title>
<!-- CDN 引入 vue 和 axios -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<div id="app">
<!-- 输入框区域 -->
<div style="height:50px">
<input type="text" placeholder="请输出电话号码" v-model="phoneNum">
<button @click="getFaceResult"> 确定 </button>
</div>
<!-- 充值面值 显示区域 -->
<div>
充值面值:<span v-for="item in faceList" :key='item'>
{{item}}
</span>
</div>
</div>
<!-- js 代码区域 -->
<script>
new Vue({
el: '#app',
data: {
phoneNum: '12345',
faceList: ["20 元", "30 元", "50 元"]
},
methods: {getFaceResult() {}}
})
</script>
</body>
</html>
为了失去用户输出的手机号,给 input 输入框增加 v -model 指令,绑定 phoneNum 变量。展现区域则是 绑定到 faceList 数组,v-for 指令进行展现,这时命令行 nodemon server 启动服务器,如果你没有装置 nodemon,能够 npm install -g nodemon 装置它。启动胜利后,在浏览器中输出 http://localhost:3000, 能够看到页面如下,展现正确
当初咱们来动静获取充值面值。当点击确定按钮时,咱们首先要依据手机号失去省和市,所以写一个办法来发送申请获取省和市,办法命名为 getLocation, 承受一个参数 phoneNum , 后盾接口名为 phoneLocation,当获取到城市地位当前,咱们再发送申请获取充值面值,所以还要再写一个办法 getFaceList, 它承受两个参数, province 和 city, 后盾接口为 faceList,在 methods 上面增加这两个办法 getLocation, getFaceList
methods: {
// 获取到城市信息
getLocation(phoneNum) {
return axios.post('phoneLocation', {phoneNum})
},
// 获取面值
getFaceList(province, city) {
return axios.post('/faceList', {
province,
city
})
},
// 点击确定按钮时,获取面值列表
getFaceResult () {}
}
当初再把两个后盾接口写好,为了演示,写的非常简单,没有进行任何的验证,只是返回前端所须要的数据。Express 写这种简略的接口还是十分不便的,在 app.use 和 app.listen 之间增加如下代码
// 电话号码返回省和市,为了模仿提早,应用了 setTimeout
app.post('/phoneLocation', (req, res) => {setTimeout(() => {
res.json({
success: true,
obj: {
province: '广东',
city: '深圳'
}
})
}, 1000);
})
// 返回面值列表
app.post('/faceList', (req, res) => {setTimeout(() => {
res.json(
{
success: true,
obj:['20 元', '30 元', '50 元']
}
)
}, 1000);
})
最初是前端页面中的 click 事件的 getFaceResult, 因为 axios 返回的是 promise 对象,咱们应用 then 的链式写法,先调用 getLocation 办法,在其 then 办法中获取省和市,而后再在外面调用 getFaceList,再在 getFaceList 的 then 办法获取面值列表
// 点击确定按钮时,获取面值列表
getFaceResult () {this.getLocation(this.phoneNum).then(res => {if (res.status === 200 && res.data.success) {
let province = res.data.obj.province;
let city = res.data.obj.city;
this.getFaceList(province, city).then(res => {if(res.status === 200 && res.data.success) {this.faceList = res.data.obj}
})
}
}).catch(err => {console.log(err)
})
}
当初点击确定按钮,能够看到页面中输入了 从后盾返回的面值列表。这时你看到了 then 的链式写法,有一点回调地区的感觉。当初咱们在有 async/ await 来革新一下。
首先把 getFaceResult 转化成一个 async 函数,就是在其后面加 async,因为它的调用办法和一般函数的调用办法是统一,所以没有什么问题。而后就把 getLocation 和
getFaceList 放到 await 前面,期待执行,getFaceResult 函数批改如下
// 点击确定按钮时,获取面值列表
async getFaceResult () {let location = await this.getLocation(this.phoneNum);
if (location.data.success) {
let province = location.data.obj.province;
let city = location.data.obj.city;
let result = await this.getFaceList(province, city);
if (result.data.success) {this.faceList = result.data.obj;}
}
}
当初代码的书写形式,就像写同步代码一样,没有回调的感觉,十分难受。
当初就还差一点须要阐明,那就是怎么解决异样,如果申请产生异样,怎么解决?它用的是 try/catch 来捕捉异样,把 await 放到 try 中进行执行,如有异样,就应用 catch 进行解决。
async getFaceResult () {
try {let location = await this.getLocation(this.phoneNum);
if (location.data.success) {
let province = location.data.obj.province;
let city = location.data.obj.city;
let result = await this.getFaceList(province, city);
if (result.data.success) {this.faceList = result.data.obj;}
}
} catch(err) {console.log(err);
}
}
当初把服务器停掉,能够看到控制台中输入 net Erorr,整个程序失常运行。