菜单

await来管理异步_javascript本领_脚本之家

2020年4月7日 - www.2138.com

昨天看了一篇vue的教程,作者用async/
await来发送异步请求,从服务端获取数据,代码很简洁,同时async/await
已经被标准化,是时候学习一下了。

问题描述

前端向后端发请求,返回res,打印res的值,使用await
res.json(),但不清楚res.json()的作用。。。

先说一下async的用法,它作为一个关键字放到函数前面,用于表示函数是一个异步函数,因为async就是异步的意思,
异步函数也就意味着该函数的执行不会阻塞后面代码的执行。 写一个async 函数

矛盾点

res中并无json(),那么res.json得到的值是哪里来的

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

问题解决

Q1:首先打印res.json()发现返回值是对象,对象的内容是后端的一个异常处理返回的值,那么res.json()是怎么拿到这个值的?
A1:不管是请求还是响应都能够包含body对象.
body也可以是多种类型的实例.如:ArrayBuffer,ArrayBufferView,Blob,string,URLSearchParams,FormData。Body类定义了以下方法
(这些方法都被 Request和Response所实现)以获取body内容.
这些方法都会返回一个被解析后的promise对象和数据.,如:json()对象,所以这个值是后端返回给前端的,存在于body中

Q2: 那么res.json呢?
A2:res.json()的作用的就是就请求的返回值的转化成json的格式。可前面的await又做了什么?
当函数执行的时候,一旦遇到 await
就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。await可以认为是
async wait的简写,因此可以理解 async
用于声明一个function是异步的,而await用于等待一个异步方法执行完成返回的值(返回值可以是一个Promise对象或普通返回的值)。

www.2138.com,Q3:await 的注意点有什么?

Q4: async/await的优势是?
A4:单一的Promise链貌似不能发现
async/await的优势,但是如果需要处理多个Promise组成的then链的时候,优势可以看出来,因为Promise是通过then链来解决多层回调的问题,现在我们又可以使用async/await来进一步优化,他们的优点就是解决多层异步回调的嵌套。使用的情况是假设我们现在有一个需求是,分多个步骤完成,每个步骤都是异步的,并且后面的异步都需要依赖于上一个异步回调返回的数据,进行往下传递。

语法很简单,就是在函数前面加上async关键字,来表示它是异步的,那怎么调用呢?async函数也是函数,平时我们怎么使用函数就怎么使用它,直接加括号调用就可以了,为了表示它没有阻塞它后面代码的执行,我们在async函数调用之后加一句console.log;

反思

async function timeout() { return 'hello world'}timeout();console.log;

action

打开浏览器控制台,我们看到了

async函数 timeout 调用了,但是没有任何输出,它不是应该返回 ‘hello
world’, 先不要着急,看一看timeout()执行返回了什么?把上面的
timeout()语句改为console.log

async function timeout() { return 'hello world'}console.log;console.log;

原来async函数返回的是一个promise对象,如果要获取到promise返回值,我们应该用then方法,继续修改代码

async function timeout() { return 'hello world'}timeout().then(result => { console.logconsole.log;

我们获取到了”hello world’,
同时timeout的执行也没有阻塞后面代码的执行,和我们刚才说的一致。

这时,你可能注意到控制台中的Promise有一个resolved,这是async函数内部的实现原理。如果async函数中有返回一个值
,当调用该函数时,内部会调用Promise.solve()方法把它转化成一个promise对象作为返回,但如果timeout函数内部抛出错误呢?
那么就会调用Promise.reject()返回一个promise对象,这时修改一下timeout函数

async function timeout { return 'hello world' } else { throw 'my god, failure' }}console.log // 调用Promise.resolve() 返回promise 对象。console.log; // 调用Promise.reject() 返回promise 对象。

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

timeout.catch(err => { console.log

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

现在写一个函数,让它返回promise对象,该函数的作用是2s之后让数值乘以2

// 2s 之后返回双倍的值function doubleAfter2seconds { return new Promise => { setTimeout => { resolve; } )}

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

async function testResult() { let result = await doubleAfter2seconds; console.log;}

现在调用testResult函数

testResult();

打开控制台,2s之后,输出了60.

现在我们看看代码的执行过程,调用testResult函数,它里面遇到了await,
await表示等一下,代码就暂停到这里,不再向下执行了,它等什么呢?等后面的promise对象执行完毕,然后拿到promise
resolve的值并进行返回,返回值拿到之后,它继续向下执行。具体到我们的代码,遇到await之后,代码就暂停执行了,等待doubleAfter2seconds
执行完毕,doubleAfter2seconds返回的promise开始执行,2秒之后,promise
resolve了,并返回了值为60,这时await才拿到返回值60,然后赋值给result,暂停结束,代码才开始继续执行,执行
console.log语句。

就这一个函数,我们可能看不出async/await的作用,如果我们要计算3个数的值,然后把得到的值进行输出呢?

async function testResult() { let first = await doubleAfter2seconds; let second = await doubleAfter2seconds; let third = await doubleAfter2seconds; 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;const app = express();// express.static 提供静态文件,就是html, css, js 文件app.use(express.static;app.listen => { console.log

再写index.html文件,我在这里用了vue构建页面,用axios发送ajax请求,为了简单,用cdn引入它们。
html部分很简单,一个输入框,让用户输入手机号,一个充值金额的展示区域,
js部分,按照vue的要求搭建了模版

 Async/await        确定    充值面值:  {{item}}      new Vue({ el: '#app', data: { phoneNum: '12345', faceList: ["20元", "30元", "50元"] }, methods: { getFaceResult 

为了得到用户输入的手机号,给input输入框添加v-model指令,绑定phoneNum变量。展示区域则是绑定到faceList数组,v-for指令进行展示,这时命令行nodemon
server启动服务器,如果你没有安装nodemon,可以npm install -g
nodemon安装它。启动成功后,在浏览器中输入

现在我们来动态获取充值面值。当点击确定按钮时,
我们首先要根据手机号得到省和市,所以写一个方法来发送请求获取省和市,方法命名为getLocation,
接受一个参数phoneNum ,
后台接口名为phoneLocation,当获取到城市位置以后,我们再发送请求获取充值面值,所以还要再写一个方法getFaceList,
它接受两个参数, province 和city, 后台接口为faceList,在methods
下面添加这两个方法getLocation, getFaceList

methods: { //获取到城市信息 getLocation { return axios.post('phoneLocation', { phoneNum }) }, // 获取面值 getFaceList { return axios.post('/faceList', { province, city }) }, // 点击确定按钮时,获取面值列表 getFaceResult () { } }

现在再把两个后台接口写好,为了演示,写的非常简单,没有进行任何的验证,只是返回前端所需要的数据。Express
写这种简单的接口还是非常方便的,在app.use 和app.listen 之间添加如下代码

// 电话号码返回省和市,为了模拟延迟,使用了setTimeoutapp.post('/phoneLocation',  => { setTimeout => { res.json({ success: true, obj: { province: '广东', city: '深圳' } }) }, 1000);})// 返回面值列表app.post('/faceList',  => { setTimeout => { res.json( { success: true, obj:['20元', '30元', '50元'] } ) }, 1000);})

最后是前端页面中的click 事件的getFaceResult, 由于axios 返回的是promise
对象,我们使用then
的链式写法,先调用getLocation方法,在其then方法中获取省和市,然后再在里面调用getFaceList,再在getFaceList的then方法获取面值列表,

// 点击确定按钮时,获取面值列表 getFaceResult () { this.getLocation .then(res => { if (res.status === 200 && res.data.success) { let province = res.data.obj.province; let city = res.data.obj.city; this.getFaceList .then(res => { if(res.status === 200 && res.data.success) { this.faceList = res.data.obj } }) } }) .catch(err => { console.log }

现在点击确定按钮,可以看到页面中输出了从后台返回的面值列表。这时你看到了then的链式写法,有一点回调地域的感觉。现在我们在有async/
await来改造一下。

首先把getFaceResult转化成一个async函数,就是在其前面加async,因为它的调用方法和普通函数的调用方法是一致,所以没有什么问题。然后就把getLocation和getFaceList放到await后面,等待执行,
getFaceResult 函数修改如下

// 点击确定按钮时,获取面值列表 async getFaceResult () { let location = await this.getLocation; if (location.data.success) { let province = location.data.obj.province; let city = location.data.obj.city; let result = await this.getFaceList; if  { this.faceList = result.data.obj; } } }

现在代码的书写方式,就像写同步代码一样,没有回调的感觉,非常舒服。

现在就还差一点需要说明,那就是怎么处理异常,如果请求发生异常,怎么处理?它用的是try/catch来捕获异常,把await放到
try中进行执行,如有异常,就使用catch进行处理。

async getFaceResult () { try { let location = await this.getLocation; if (location.data.success) { let province = location.data.obj.province; let city = location.data.obj.city; let result = await this.getFaceList; if  { this.faceList = result.data.obj; } } } catch { console.log; } }

现在把服务器停掉,可以看到控制台中输出net Erorr,整个程序正常运行。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图