先说一下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 之间增加如下代码
// 电话号码返回省和市,为了模仿提早,应用了setTimeoutapp.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,整个程序失常运行。