使用async/await来处理异步(vue+node的实例)

https://www.jianshu.com/p/ab767311589d

使用async/await来处理异步(vue+node的实例)

用async/await 来处理异步
近期项目中大量使用async,从服务器获取数据,解决一些并发传参问题。代码很简单。在此也看了一些博客,现在async/await已经大范围让使用,是时候总结一波啦。
先说一下async的语法,它作为一个关键字放在函数前面,用于表示函数是一个异步函数,因为async就是异步的异步,异步函数也就是意味着这个函数的执行不会阻塞后面代码的执行。简单的写一个async函数

async function hello(){
    return 'hello world';
}

语法很简单,就是在函数前面加上async,他就成异步函数啦。怎么去调用呢,其实一样,平时怎么使用函数我们就怎么去调用它,直接加括号就可以啦,为了表示它没有阻塞后面的代码我们写个案例

async function hello(){
  return 'hello world';
}
console.log(hello());
console.log('我是先执行的');

image.png

好像没有什么用,别急,首先我们看到hello()返回的是一个promise对象,其次它好像没有去异步执行。
async异步函数返回的是一个promise对象,如果要获取到promise返回值,我们就应该使用.then方法。

async function hello(){
  return 'hello world';
}
hello().then(result=>{
  console.log(result);
})
console.log('我是先执行的');

image.png

然后就没问题啦,一个简单的异步函数就OK啦,hello执行的时候没有阻塞后面代码的执行,和我们之前说的一样。
你可能注意到控制台中的promise有一个resolved,这是async函数内部的实现原理,如果async函数中返回一个值,当调用该函数时,内部会调用promise.solve()方法把它转化成一个promise()对象作为返回,但如果hello函数内部发生错误呢?那么就会调用promise.reject()返回一个promise对象,这时修改一下hello()函数

async function hello(flag){
   if(flag){
      return 'hello world';
   }else{
       throw 'happen Error';
   }
}
console.log(hello(0));
console.log(hello(1));

image.png

如果函数内部发生错误,promise对象有一个catch方法进行捕获。

hello(0).catch(err=>{
  console.log(err);
})

image.png

async是差不多啦,我们再来熟悉一下await关键字,await是等待的意思,那么它在等待什么呢,它后面跟着什么呢?其实它后面可以放任何表达式,不过我们更多放的是一个promise对象的表达式。注意await关键字,只能放在async函数里面!!!

function awaitMethod(num){
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve(2*num);
    },2000)
  })
}

我们再写一个async函数,从而可以使用await关键字,await后面放置的是返回promise对象的一个表达式,所以它后面可以写上awaitMethod函数的调用。

async function test (){
  let result = await awaitMethod(30);
  console.log(result);
}

然后我们调用这个函数

test();

2秒钟之后控制台输出60。
现在我们看看代码的执行过程,调用test函数时,它遇到了里面的await关键字,await代表等待,代码到这里会暂停,它在等什么呢,等待后面的promise对象执行完成,然后拿到promise的返回值,拿到返回值之后它继续往下执行,直到console.log()执行。
就这一个函数,或许我们看不出来asycn和await的作用,如果我们要计算3个数的值,然后把他们的值加起来输出,或许就看的明显啦。

async function test(){
    let result = await awaitMethod(30);
    let result1 = await awaitMethod(50);
    let result2 = await awaitMethod(30);
    console.log(result+result1+result2 );
}

6秒之后控制台,输出220,我们可以看到,写异步代码的就像写同步代码一样啦,再也没有什么回调地狱这一说啦。
下来,我们使用node+vue写一个简单的实例,什么实例呢,这个实例需要用户先拿到省和市,然后在根据省和市找到充值的面值,进行展示。
首先我们要模拟一下后台接口,我们新建一个node项目,新建一个文件夹AsyncAndAwait,然后npm init -y新建package.json文件,npm install express --save安装一下express的依赖,然后在新建一个server.js作为服务器代码,public文件夹作为静态文件放置的位置,在public文件夹里面放index.html文件,这个文件的目录

image.png

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');
})

接下来写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指令进行展示,这是命令行node server启动服务器,启动成功之后,在浏览器输入http://localhost:3000,可以看到页面如下,展示正确

image.png


我们再来动态获取充值面值。当用户点击按钮时,我们首先要根据手机号得到省和市,所以写一个方法来发生请求获取省和市,方法命名为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,因为它的调用方法和普通函数的调用方法是一致的,所以没有什么问题。然后就把 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);
  }
}

OK啦,这应该就完美啦。

猜你喜欢

转载自blog.csdn.net/wwf1225/article/details/115301338