异步问题除了可以使用前面学到的Promise
来解决之外,我们还可以用async/await
来搞定。
async/await
是ES7中新增的新语法。虽然现在最新的chrome浏览器已经支持了该语法,但是在实际使用中,我们需要在构建工具中配置对该语法的支持才能放心使用。因此如果你目前的开发经验还没有涉及到构建工具的使用,你可以暂时跳过该语法的学习。
接下来的知识会配合ES6的语法进行讲解,如果你还没有学习ES6的语法,也可以暂时跳过
在函数声明的前面,加上关键字async
,这就是async
的具体使用。
async function fn() {
return 30;
}
// 或者
const fn = async () => {
return 30;
}
我们可以打印出fn函数的运行结果
console.log(fn());
// result
Promise = {
__proto__: Promise,
[[PromiseStatus]]: "resolved",
[[PromiseValue]]: 30
}
发现fn函数运行返回的是一个标准的Promise
对象。因此我们可以猜想到,async
其实是Promise的一个语法糖,目的是为让写法更加简单。因此我们也可以使用Promise
的相关语法来处理后续的逻辑。
fn().then(res => {
console.log(res); // 30
})
await的含义为等待。意思就是代码需要等待await后面的函数运行完并且有了返回结果之后,才继续执行下面的代码。这正是同步的效果。
但是我们需要注意的是,await关键字只能在async函数中使用。并且await后面的函数运行后必须返回一个Promise对象才能实现同步的效果。
当我们使用一个变量去接收await的返回值时,该返回值为Promise中resolve传递出来的值(也就是PromiseValue)。
// 定义一个返回Promise对象的函数
function fn() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(30);
}, 1000);
})
}
// 然后利用async/await来完成代码
const foo = async () => {
const t = await fn();
console.log(t);
console.log('next code');
}
foo();
// result:
// 30
// next code
运行这个例子我们可以看出,当在async函数中,运行遇到await时,就会等待await后面的函数运行完毕,而不会直接执行next code。
如果我们直接使用then方法的话,想要达到同样的结果,就不得不把后续的逻辑写在then方法中。
const foo = () => {
return fn().then(t => {
console.log(t);
console.log('next code');
})
}
foo();
很显然如果使用async/await的话,代码结构会更加简洁,逻辑也更加清晰。
异常处理
在Promise中,我们知道是通过catch的方式来捕获异常。而当我们使用async时,则通过try/catch来捕获异常。
function fn() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('some error.');
}, 1000);
})
}
const foo = async () => {
try {
await fn();
} catch (e) {
console.log(e); // some error
}
}
foo();
如果有多个await函数,那么只会返回第一个捕获到的异常。
function fn1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('some error fn1.');
}, 1000);
})
}
function fn2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject('some error fn2.');
}, 1000);
})
}
const foo = async () => {
try {
await fn1();
await fn2();
} catch (e) {
console.log(e); // some error fn1.
}
}
foo();
在实践中我们遇到异步场景最多的就是接口请求,那么这里就以jquery中的$.get为例简单展示一下如何配合async/await来解决这个场景。
// 先定义接口请求的方法,由于jquery封装的几个请求方法都是返回Promise实例,因此可以直接使用await函数实现同步
const getUserInfo = () => $.get('xxxx/api/xx');
const clickHandler = async () => {
try {
const resp = await getUserInfo();
// resp为接口返回内容,接下来利用它来处理对应的逻辑
console.log(resp);
// do something
} catch (e) {
// 处理错误逻辑
}
}
为了保证逻辑的完整性,在实践中try/catch必不可少。