JavaScript
中的async/await
是AsyncFunction特性中的关键字。(读者可自行查阅改链接中内容)
async定义的异步函数,通过使用 await,让异步函数的表现更像是同步函数。不用再层层回调。它是Generator函数的语法糖。其中async来声明该函数里有异步操作。await表示紧跟在后面的表达式需要等待结果
与Generator
函数比优点:
- 内置执行器。
Generator
函数的执行必须依靠执行器,而Aysnc
函数自带执行器,调用方式跟普通函数的调用一样‘ - 更好的语义。
async
和await
相较于*
和yield
更加语义化 - 更广的适用性。
co 模块
约定,yield
命令后面只能是Thunk 函数
或Promise对象
。而async
函数的await
命令后面则可以是Promise
或者 原始类型的值(Number,string,boolean
,但这时等同于同步操作) - 返回值是
Promise
。async
函数返回值是Promise 对象
,比Generator
函数返回的Iterator 对象
方便,可以直接使用then()
方法进行调用
如果你还不了解Generator
函数,你可以查阅下面:
先看几个demo:
1、之前不用async的异步Promise操作
定义个获取用户信息的函数:
function getUserInfo() {
return new Promise((resolve, reject) => {
fetch('http://abc.com')
.then(res => {
reslove(res)
}, error => {
reject(error)
})
})
}
function showUserInfo() {
getUserInfo()
.then(res => {
//...
}, error => {
//....
})
}
2、使用Generator 方式
function* showUserInfo() {
const user = yield getUserInfo()
return user
}
const g = showUserInfo()
cosnt res = g.next().value()
result.then(res => {
//..
}, error => {
//..
})
Generator
的方式解决了 Promise
的一些问题,流程更加直观、语义化。但是 Generator
的问题在于,函数的执行需要依靠执行器,每次都需要通过 g.next()
的方式去执行。
3、使用async/await
async function showUserInfo() {
const res = await getUserInfo();
return res
}
showUserInfo().
then(res => {
//..
})
看到这里你可能还有点一脸懵逼,别着急,接着往下看。
4、语法
async
函数内部返回一个Promise
对象。其在内部返回(return
)的值会成为then
方法的参数。
async function f() {
return 'hello world'
};
f().then( (v) => console.log(v)) // hello world
如果async
函数内部抛出异常,则返回的Promise
状态会变为reject
状态。抛出的错误会被catch
方法捕捉到。
async function e(){
throw new Error('error');
}
e().then(v => console.log(v))
.catch( e => console.log(e));
需要注意的是:async
返回的Promise
对象,必须等到内部所有的await
命令的Promise
对象执行完毕,才会发生状态改变。也就是说只有当async
内部的异步操作都成功执行完,才会执行then
里的方法
const delay = timeout => new Promise(resolve=> setTimeout(resolve, timeout));
async function f(){
await delay(1000);
await delay(2000);
await delay(3000);
return 'done';
}
f().then(v => console.log(v)); // 等待6s后才输出 'done'
正常情况下,await
命令后面是一个 Promise
对象,返回该对象的结果。如果不是 Promise
对象,就直接返回对应的值(如果没有返回值,那么默认返回值将是undefined
)
async function f() {
return await 1
};
f().then( (v) => console.log(v)) // 1
如果此时返回的 reject
的状态,就会被catch
捕获。
async function foo() {
return Promise.reject(123)
}
foo().then().catch(e => console.log(e)) //控制台打印出 '123'
5、async函数的错误处理
let a;
async function f() {
await Promise.reject('error');
a = await 1; // 这段 await 并没有执行
}
f().then(v => console.log(a)).catch(err => console.log(err))
只要async
函数中的一个 await出现reject
状态,那么后面的await
就不会再执行。
此时错误处理使用try/catch来处理。
let a;
async functoin f() {
try {
await Promise.reject('error')
} catch (err) {
console.log(err)
}
a = await 1;
return a
}
f().then(v => console.log(a)).catch(err => console.log(err))
如果有多个await
,可以都放在try/catch
中。
6、一些代码来加强认知
async function asyncFun() {
return '我后执行'
}
asyncFun().then(v => console.log(v))
console.log('我先执行');
这段代码会先打印出我先执行
,再打印我后执行.
因为声明了asyncFun
为异步函数。该函数虽然先执行了,但是不会阻断后面代码的执行。同步代码先执行。
注意对比下面代码,理解执行顺序。
const timeOut = timeOut => {
return new Promise(resolve => setTimeout(resolve, timeOut))
}
async function timeFun() {
await timeOut(1000)
await timeOut(2000)
return 'end..'
}
timeFun().then(v => console.log(v))
//打印结果
[Running] node "c:\lhch\work\Projects\own\vue\async.js"
end..
[Done] exited with code=0 in 3.093 seconds
//可以看出 在3s后打印出了end.. ,这是使用了await的效果
7、async
里的函数必须将结果return
async
里必须将结果return
,如果不返回结果,那么不管是resolve
或者reject
状态,其值都是undefined
。这里建议使用箭头函数。
这是正确的写法,控制台会打印errrrr
async function errFun() {
return Promise.reject('errrrr')
}
errFun().then().catch(v => console.log(v))
错误的写法:
async function errFun() {
Promise.reject('errrrr')
}
这里执行errFun
控制台会直接报错。
需要深入理解的: 只要async
返回的值不是异常或者reject
,就会判定成功。在async
函数里,可以return
各种类型的值。
只有出现如下情况的时候,才会判定失败reject
:
- 内部使用未声明的变量或者函数
- 内部直接抛出错误(
throw new Error
)或者返回reject
状态(retrun Promise.reject
) - 函数方法执行出错
async function c() {
return '接受到了吧'
}
c().then(v => console.log(v))
上面代码控制台会打印出 '接受到了吧'
,同时显示://Promise { <resolved>: undefined }
为什么 resolved
的是undefined
呢,因为then
后是执行的console
语句,这里没有返回值。
一个小总结:
- 是一种编写异步代码的新方法。之前异步代码的方案是
callback
和promise
。 - 建立在
promise
的基础上,与promise
一样也是非阻塞的。 async/await
让异步代码看起来、表现起来更像同步代码。这正是其魅力(作用)所在。
建议您读此文的时候,自己手敲代码,加深理解.