理解异步函数async和await的用法
Posted 铁锤妹妹@
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了理解异步函数async和await的用法相关的知识,希望对你有一定的参考价值。
定义
async作为一个关键字放在函数前面,表示该函数是一个异步函数,异步函数意味着该函数的执行不会阻塞后面代码的执行;而 await 用于等待一个异步方法执行完成;
async/await的作用就是使异步操作以同步的方式去执行
一. 关于async
async的用法,语法很简单,在函数前面加上async关键字,表示函数是异步的。
async function timeout()
return 'hello world!'
只有一个作用,他的调用会返回一个promise对象。
那怎么调用呢?async 函数也是函数,平时我们怎么使用函数就怎么使用它,直接加括号调用就可以了,为了表示它没有阻塞它后面代码的执行,我们在async 函数调用之后加一句console.log;
async function timeout()
return 'hello world!'
timeout()
console.log('我虽然在后面,但是先执行')
打印结果:
发现 timeout() 函数虽然调用了,但是没打印 hello world!; 先不要着急, 看一看timeout() 返回了什么? 把上面的 timeout() 语句改为console.log(timeout())
打印结果:
原来async 函数返回的是一个promise 对象,并且Promise还有state和result,如果async函数中有返回值,当调用该函数时,内部会调用Promise.resolve()方法把它转化成一个promise对象作为返回,
但如果timeout函数内部抛出错误呢? 那么就会调用Promise.reject() 返回一个promise 对象
async function timeout()
throw new Error('rejected');
console.log(timeout());
就会调用Promise.reject() 返回一个promise 对象
那么要想获取到async 函数的执行结果,就要调用promise的then 或 catch 来给它注册回调函数
继续修改代码
async function timeout()
return 'hello world!'
timeout().then(val =>
console.log(val)
)
console.log('我虽然在后面,但是先执行')
打印结果:
我们获取到了"hello world!’, 同时timeout的执行也没有阻塞后面代码的执行,和 我们刚才说的一致。
如果async 函数执行完,返回的promise 没有注册回调函数,比如函数内部做了一次for 循环,你会发现函数的调用,就是执行了函数体,和普通函数没有区别,唯一的区别就是函数执行完会返回一个promise 对象。
async function timeout ()
for (let index = 0; index < 3; index++)
console.log('async', +index)
console.log(timeout())
console.log('outer')
二. 关于await
1) await 到底在等啥?
async 关键字差不多了,最重要的就是async函数的执行会返回promise对象,并且把内部的值进行promise的封装。如果promise对象通过then或catch方法又注册了回调函数,async函数执行完以后,注册的回调函数就会放到异步队列中,等待执行。
如果只是async,和promise差不多,但有了await就不一样了,await关键字只能放到async函数里面,await是等待的意思,那么它等待什么呢?它后面跟着什么呢?其实await不仅仅用于等Promise对象,还可以等任意表达式,所以await后面实际是可以接普通函数调用或者直接量的,不过我们更多的是放一个返回promise 对象的表达式。他等待的是promise对象执行完毕,并返回结果。
//所以下面这个示例完全可以正确运行
function getSomething ()
return 'something'
async function testAsync ()
return Promise.resolve('hello async')
async function test ()
const v1 = await getSomething()
const v2 = await testAsync()
console.log(v1, v2)
test()
2) await 等到了要等的,然后呢?
await 等到了它要等的东西,一个 Promise 对象,或者其它值,然后呢?
- 如果它等到的不是一个Promise对象,那么await表达式的运算结果就是它等到的东西。
- 如果它等到的是一个Promise对象,await就忙起来了,它会阻塞函数后面的代码,等着Promise对象resolve,然后得到resolve的值,作为await表达式的运算结果。
3) async/await 帮我们干了啥?
做个简单的比较
现在举例,用 setTimeout 模拟耗时的异步操作,先来看看不用 async/await 会怎么写
function takeLongTime ()
return new Promise(resolve =>
setTimeout(() =>
resolve('long_time_value'), 1000
)
)
takeLongTime().then(val =>
console.log(val, 'val')
)
如果改用 async/await 呢,会是这样
function takeLongTime ()
return new Promise(resolve =>
setTimeout(() =>
resolve('long_time_value'), 1000
)
)
async function test ()
let v = await takeLongTime()
console.log(v, 'v')
test()
眼尖的已经发现 takeLongTime ()
没有申明为async
。实际上takeLongTime ()
本身就返回Promise对象,加不加async结果都一样。
4) await 优势在于处理 then 链,使代码看起来像同步代码一样,下面是实例应用
现在写一个函数,让它返回promise 对象,该函数的作用是2s 之后让数值乘以2
// 2s 之后返回双倍的值
function doubleAfter2seconds (num)
return new Promise((resolve, reject) =>
setTimeout(() =>
resolve(num * 2)
, 2000)
)
现在再写一个async 函数,从而可以使用await 关键字, await 后面放置的就是返回promise对象的一个表达式,所以它后面可以写上 doubleAfter2seconds 函数的调用
async function testResult()
let result = await doubleAfter2seconds(30);
console.log(result); //2s后打印60
testResult();
代码的执行过程
调用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);
testResult()
6秒后,控制台输出220, 我们可以看到,写异步代码就像写同步代码一样了,再也没有回调地域了。
这里强调一下,当js引擎在等待promise.resolve的时候,他并没有真正的暂停工作,它可以处理其他的一些事情,如果我们在testResult函数后面继续执行其他代码,比如console.log一下,会发现console.log代码先执行。
async function testResult()
let first = await doubleAfter2seconds(30);
let second = await doubleAfter2seconds(50);
let third = await doubleAfter2seconds(30);
console.log(first + second + third);
testResult()
console.log('我先执行!!!')
先输出 “我先执行!!!”,6s后输出计算结果。
总结:
- async 函数
1)函数的返回值为Promise对象
2)Promise对象的结果由async函数执行的返回值决定- await 表达式
1)await右侧的表达式一般为promise对象, 但也可以是其它的值
2)如果表达式是promise对象,await就忙起来了,它会阻塞函数后面的代码,等着Promise对象resolve,然后得到resolve的值,
作为await表达式的运算结果。
3)如果表达式是其它值, 直接将此值作为await的返回值async
和await
基于promise的。使用async的函数将会始终返回一个 promise 对象。这一点很重要,要记住,可能是你遇到容易犯错的地方。- 在使用
await
的时候我们只是暂停了函数,而非整段代码。- async和await是
非阻塞的
- 仍然可以使用
Promise
,例如Promise.all()
.- 注意
1)await
必须写在async
函数中, 但async函数中可以没有await
2)如果await的promise失败了, 就会抛出异常, 需要通过try…catch
来捕获处理
学习过程中参考了:
用 async/await 来处理异步
vue中异步函数async和await的用法
理解 JavaScript 的 async/await
以上是关于理解异步函数async和await的用法的主要内容,如果未能解决你的问题,请参考以下文章