使用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啦,这应该就完美啦。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容