先说一下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,整个程序失常运行。
发表回复