学完了Promise,我们知道可以用then链来解决多层回调问题,但是这还不是最理想的操作,我们需要调用很多个then链才能达到要求,那么有没有一种更简便代码量更少的方式达到then链相同的结果呢?async和await就很好地解决了这个问题,首先用async声明一个异步函数,然后再用await等待异步结果,把以前then链的结果放到直接放在await,非常方便。
那么,async和await原理是什么呢?为什么可以用这样的语法来优化then链呢?
那么async/await是什么呢?
async/await其实是Promise的语法糖,它能实现的效果都能用then链来实现,也可以说它是为优化then链而开发出来的。从字面上来看,async是异步的简写,await是等待的意思,await 用于等待一个异步任务执行完成的结果。因此,我们可以总结出来:
- async/await 是一种编写异步代码的新方法(以前是采用回调和 promise)。
- async/await 是建立在 promise 的基础上。
- async/await 像 promise 一样,也是非阻塞的。
- async/await 让异步代码看起来、表现起来更像同步代码。
一:只使用anysc
我们可以一起来看一下async,async修饰的函数就是异步函数,该函数的返回值是promise对象。
//async修饰的函数就是异步函数,该函数的返回值是promise对象。
async function funA() {
return ("hellow,我是小妲己")
}
let p1 = funA();
console.log(p1);//打印出来的p1是一个promise对象
上面代码的打印结果:
async修饰函数后,函数的返回值是promise对象,因此,它也是可以使用then方法:
async function funA() {
return ("hellow,我是小妲己")
}
let p1 = funA();
console.log(p1);//promise对象
p1.then(function(str) {
console.log("then", str);//then hellow,我是小妲己
});
那么,如果我们想要用promise来实现上述代码,应该是怎么写的呢?一起来看看吧:
function funA() {
return new Promise(function(resolve, reject) {
resolve("hellow,我是小妲己")
})
}
funA().then(function(str) {
console.log("funA", str); //funA hellow,我是小妲己
})
由上面代码我们可以看到,它们的执行结果是一样的。那么进一步说明了:
Async修饰的函数的返回值会作为resolve对应函数的参数。
二:使用async和await结合,获取resolve里面的值
首先,await只能写在async修饰的函数里。
Await是等待的意思,await修饰的代码会等待。在函数里,碰到await修饰的代码时,await朝后的代码都会等待。也就是说,必须得等待await后面的Promise处理完成才能继续。
一般来说:await后面接一个Proimse对象,或者返回Promise对象的函数。
await 修饰函数后,那么,返回值变成了Promise对象中resolve的参数。
我们来写一个示例:
async function fn() {
console.log("await前");
// await 修饰的 Promise对象的返回值是 resolve的参数,所以我们不用再写then了。
let res = await new Promise((resolve, reject) => {
setTimeout(function() {
resolve("欢迎来到");
}, 1000)
});
//res就相当于拿到了resolve中的参数
console.log("await后:" + res); //await后:欢迎来到
}
fn();
console.log('虽然在后面,但是我先执行');
上面代码中,我们可以await后面的Promise对象写在一个函数里。
function testf() {
return new Promise((resolve, reject) => {
setTimeout(function() {
resolve("欢迎来到");
}, 1000);
});
}
async function fn() {
console.log("await前");
// await 修饰的 Promise对象的返回值是 resolve的参数,所以我们不用再写then了
let res = await testf();
console.log("await后:" + res); //await后:欢迎来到
}
fn();
console.log('虽然在后面,但是我先执行');
可以看到,这样我们依然可以拿到resolve中的参数。
三:使用anysc和await结合,获取reject里面的值
使用try catch
格式:
try{
// 若干句代码;这些代码里,只要碰到错误,就会跳到catch里。即就是:碰到错误后,try里处于错误行后面的代码不再执行。
}catch(){
// 出错,
}
//===================================如============================
try {
let res1 = await ajax();
// res1就是 resolve的参数
let res2 = await ajax();
// res2就是 resolve的参
} catch (err) {
//err: 如果说第一次ajax请求失败, 那么err就是第一次ajax请求里reject的参数;
// 如果说第二次ajax请求失败, 那么err就是第二次ajax请求里reject的参数
}
示例一:
function getFn() {
return new Promise((resolve, reject) => {
setTimeout(function() {
if (false) {
resolve("hhhh");
} else {
reject("出错了");
}
}, 1000)
})
}
async function fn() {
console.log("await前")
try {
let res = await getFn();
console.log("await后(成功):" + res) //此处拿到的是resolve的参数;
} catch (error) {
console.log("await后(失败):" + error) //此处拿到的是reject的参数
}
}
fn();
示例二:发送两个ajax请求,两个ajax请求放在同一个try catch里,那么,哪个出错了,catch里拿到的就是哪个里的reject的参数。
try {
let sendStr = `username=${$("username").value}&userpass=${$("userpass").value}`;
let res = await ajax03({
method: "post",
url: "loginCheck03.php",
params: sendStr
});
success(res);
let res2 = await ajax03({
url: "loadMore.php",
params: "pageIndex=2"
});
show(res2);
} catch (error) {
console.log("error:", error); //第一次请求出错,那么拿到的就是第一次请求的reject的参数;第二次出错,就是第二次的reject的参数;
}
总结一下:async/await的优点
1.方便级联调用:即调用依次发生的场景;
2.同步代码编写方式:Promise使用then函数进行链式调用,一直点点点,是一种从左向右的横向写法;async/await从上到下,顺序执行,就像写同步代码一样,更符合代码编写习惯;
3.多个参数传递:Promise的then函数只能传递一个参数,虽然可以通过包装成对象来传递多个参数,但是会导致传递冗余信息,频繁的解析又重新组合参数,比较麻烦;async/await没有这个限制,可以当做普通的局部变量来处理,用let或者const定义的块级变量想怎么用就怎么用,想定义几个就定义几个,完全没有限制,也没有冗余工作;
4.同步代码和异步代码可以一起编写:使用Promise的时候最好将同步代码和异步代码放在不同的then节点中,这样结构更加清晰;async/await整个书写习惯都是同步的,不需要纠结同步和异步的区别,当然,异步过程需要包装成一个Promise对象放在await关键字后面;
5.基于协程:Promise是根据函数式编程的范式,对异步过程进行了一层封装,async/await基于协程的机制,是真正的“保存上下文,控制权切换……控制权恢复,取回上下文”这种机制,是对异步过程更精确的一种描述;
6.async/await是对Promise的优化:async/await是基于Promise的,是进一步的一种优化,不过在写代码时,Promise本身的API出现得很少,很接近同步代码的写法。
总结一下:async/await的缺点
缺点在于滥用 await
可能会导致性能问题,因为 await
会阻塞代码,也许之后的异步代码并不依赖于前者,但仍然需要等待前者完成,导致代码失去了并发性