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