async-await
异步处理和使用
好不容易看完了Promise
心想着可算是能用一下了,结果就看到了各种标题为:有了async-await以后promise还有必要学习吗?
、async await优于promise的几个特点
。。。然后我就懵圈了,我的内心emmmmm。
原来,async-await
是promise
和generator
的语法糖,只是为了让我们书写代码更加流畅,增强代码的可读性,简而言之:async-await
是建立在promise
机制之上的,并不能取代promise
。
async-await
基本使用
async
函数返回一个Promise
对象,可以使用then
方法添加回调函数。
当函数执行的时候,一旦遇到await
就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
async function asyncPrint(value, ms) {
await timeout(ms)
console.log(value)
}
asyncPrint('hello world', 2000);
//hello world (2s后)
上面代码指定2000毫秒以后,输出hello world
。同时函数asyncPrint()
执行结果返回了一个promise
对象。
async
async
用来表示函数是异步的,定义的函数会返回一个promise
对象,可以使用then
方法添加回调函数,而async
函数内部return
语句返回的值,就会成为then
方法回调函数的参数。针对上面的asyncPrint
例子来看,如果我们直接使用then
会发现什么都没有
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
async function asyncPrint(value, ms) {
await timeout(ms)
console.log(value)
}
asyncPrint('hello world', 2000).then((value)=>{console.log(value)})
//hello world (2s后)
//undefined
因为函数asyncPrint
内部没有return
语句返回值,那我们加上return
试一下
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}
async function asyncPrint(value, ms) {
await timeout(ms)
console.log(value)
return 'done'
}
asyncPrint('hello world', 2000).then((value)=>{console.log(value)})
//hello world (2s后)
//done
其实就是相当于执行了Promise.resolve('done')
,如果没有return
就相当于Promise.resolve()
。
await
await
后面可以跟任何JS表达式,虽然await
可以跟很多类型的东西,但是最主要的意图是用来等待Promise
对象的状态被resolved
,如果await
的是promise
对象会造成异步函数停止执行并等待promise
解决,如果等到的是正常的表达式就立即执行,还是开头的那个例子,如果await
的不是promise
对象会怎么样
function timeout(ms) {
setTimeout(()=>{}, ms)
}
async function asyncPrint(value, ms) {
await timeout(ms)
console.log(value)
}
asyncPrint('hello world', 2000);
//hello world (立即)
因为await
后面不是promise对象了,也就不需要等待结果返回了,所以到这就直接往下执行了。
再来一个例子
function sleep(second) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('enough sleep~');
}, second);
})
}
function normalFunc() {
console.log('normalFunc');
}
async function awaitDemo() {
await normalFunc();
console.log('something, ~~');
let result = await sleep(2000);
console.log(result);// 两秒之后会被打印出来
}
awaitDemo();
// normalFunc
// something, ~~
// ---2s后---
// enough sleep~
第一个await
后面是一个正常表达式所以直接执行后继续往下执行,然后再遇到第二个await
,这个await
后面是一个异步操作,所以需要等待结果返回并执行后再继续往下执行,也就是过了2s以后再打印最后的值。
回调实例
举例说明,假设有三个请求需要发生,第三个请求是依赖第二个请求的解析,第二个请求是依赖第一个请求的解析
如果要用ES5实现就会有三个回调
如果用Promise就会至少有三个then
一个是横向代码很长,一个是纵向代码很长,如果用async-await
来实现呢?
//我们仍然使用 setTimeout 来模拟异步请求
function sleep(second, param) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(param);
}, second);
})
}
async function test() {
let result1 = await sleep(1000, 'req01');
console.log(result1)
let result2 = await sleep(1000, 'req02' + result1);
console.log(result2)
let result3 = await sleep(1000, 'req03' + result2);
console.log(result3)
}
test();
//req01 --1s后--
//req02req01 --2s后--
//req03req02req01 --3s后--
难怪说async-await
是promise
的语法糖了,其实还是promise
,只是代码阅读起来让你觉得它好像是一个同步请求,不用那么多回调或者then
了。amazing!!!
小心并行处理
如果有多个await命令后面的异步操作之间不存在相互依赖的关系,那我们当然就不能使用上面实例中的方法来使用async-await了
例如有三个异步请求需要发送,但是相互之间没有关联,要做的其实就是当所有异步请求结束后清除界面上的loading,如果我们像上面那样使用当最后一个请求结束后清除loading,其实等待的时间就是三个请求时间之和:
function sleep(second) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('sth');
}, second);
})
}
async function clearLoad() {
await sleep(1000);
await sleep(1000);
await sleep(1000);
console.log('清除loading啦');
}
clearLoad();
// 清除loading啦 --3s后--
其实这里真正的需求是当最慢的那个请求发送结束了,就可以清除loading了,所以改良后是这样:
function sleep(second) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('sth');
}, second);
})
}
async function clearLoad() {
let p1 = sleep(1000);
let p2 = sleep(1000);
let p3 = sleep(1000);
await Promise.all([p1, p2, p3]);
console.log('清除loading啦');
}
clearLoad();
// 清除loading啦 --1s后--
所以,以上可以得出:async-await
是promise
的语法糖,让我们书写代码更加流畅,增强代码的可读性,它是建立在promise
机制之上的,并不能取代promise
。