这周四,部门分享会。一位同事展示了运用node.js去制作爬虫,通过调用对方的接口爬取数据。由于如果通过node.js去大量的异步请求对方的数据,将会导致对方服务器崩溃,无法响应。因此同事通过async/await将代码改造成同步的方式,一个接着一个请求,顺序的去请求对方接口,得到数据。因此特地研究了一下async和await关键词。
什么是async和await
async
async用来修饰函数,用于将一个函数变成为异步函数,返回值是一个Promise。
被async修饰的函数有return的值
如果被async修饰的函数有return的值,那么return的值会成为Promise.resolve(value)的参数返回。可能不好理解,看下面的例子:
async function test(){
return "有return的情况下的返回值"
}
//执行test(),返回了一个Promise
test().then((value)=>{
console.log(value) //此处Promise.resolve的参数,就是test函数return的值
})
控制台输出结果为:
有return的情况下的返回值
async修饰的函数没有return值
如果async修饰的函数没有return值,那么Promise.resolve(value)中的value返回的是undefined,看下面的例子:
async function test(){
}
test().then((value)=>{
console.log(value) //此处Promise.resolve的参数,由于test函数没有return值,所以是undefined
})
控制台输出结果为:
undefined
await
await字面意思等待,它用来等待异步函数执行完毕。await会阻塞线程的执行,所以当使用await的时候,必须在被async修饰的异步函数中执行。
在一个普通函数的调用前面加await
在一个普通函数的调用前面加await,它会立马将普通函数的返回值返回回去,看下面的例子:
----------------------------有返回值-----------------------------------------
function sayHi(){
return "hi,world";
}
async function test(){
const hi=await sayHi();
console.log(hi);
}
test()
//控制台输出:hi,world
----------------------------没有返回值-----------------------------------------
function sayHi(){
var hi= "hi,world";
}
async function test(){
const hi=await sayHi();
console.log(hi);
}
test()
//控制台输出:undefined
我们可以看到,在async函数中,在普通函数的调用前面加await,会将普通函数的返回值返回回去,如果该普通函数执行后没有返回值,则返回undefined。
在被async修饰的函数执行前加await
在被async修饰的函数执行前加await,那么会等待async返回的Promise执行完毕,如果Promise执行成功,状态从Pending=》resolved,那么就会返回Promise.resolve(value)中的value值,即async 函数中的return值。看下面的例子:
async function sayHi(){
return "hi,world";
}
async function test(){
const hi=await sayHi();
console.log(hi);
}
test()
控制台输出结果:hi,world。
在Promise对象前使用await
在Promise对象前使用await,我们都知道async返回的是一个Promise对象,那我们为什么还要再来讨论await修饰Promise对象呢?
那是因为async返回的Promise对象我们并没有指定它的then()方法,而再次来讨论Promise对象前面添加await修饰符主要是想帮助大家更深刻的理解:await修饰Promise对象,它的返回值是什么?看一下下面的例子,我们猜测一下控制台输出的结果。
async function test(){
const hi=await new Promise(function(resolve,reject){
resolve("hi,world")
}).then((value)=>{
return "hello,world"
});
console.log(hi);
}
test()
上面这段代码在控制台中输出的结果是:hello,world。
WTF!!?你不是说返回的是Promise.resolve(value)中的value值吗?
客官冷静,冷静。
由于async函数只是返回一个Promise对象,并没有指定Promise.then()方法,而用await修饰async的调用,await帮助我们添加了Promise.then(),而且将Promise的状态从Pending=》resolved,下面hi等价于myHi:
async function sayHi(){
return "hi,world";
}
async function test(){
const hi=await sayHi();
const myHi=await new Promise((resolve,reject)=>{
resolve("hi,world")
}).then((value)=>{
return value;
})
console.log(hi); //输出:hi,world
console.log(myHi) //输出:hi,world
}
test()
所以在说await修饰的async函数时,我说返回的值是是Promise.resolve(value)中的value值。
如果此时async修饰的函数没有返回值,那么此时的value值就是undefined,所以await修饰的返回值也是undefined。关于这一点,我就不再展示了,大家可以自己尝试一下。
再看下面的代码:
async function sayHi(){
return "hi,world";
}
async function test(){
const hi=await sayHi();
const myHi=await new Promise((resolve,reject)=>{
resolve("hi,world")
}).then((value)=>{
return "hello,world"; //修改成return "hello,world"
})
console.log(hi); //输出:hi,world
console.log(myHi) //输出:hello,world
}
test()
此时我们在Promise.then()中指定Promise.resolve(value)返回的不再是value,而是我们指定的内容hello,world,那么此时await的值就是hello,world。
如果当Promise有多个Promise.then()方法的时候,返回最后一个Promise.then()的返回值。如果没有返回值,则返回undefined。看下面这段代码:
async function sayHi(){
return "hi,world";
}
async function test(){
const hi=await sayHi();
const myHi=await new Promise((resolve,reject)=>{
resolve("hi,world")
}).then((value)=>{
return "hello,world";
}).then((value)=>{
return "hi,大麦"
})
console.log(hi); //输出:hi,world
console.log(myHi) //输出:hi,大麦
}
test()
如果我们的Promise状态是从pending=》rejected,那此时await的返回值是什么呢?
async function sayHi(){
return "hi,world";
}
async function test(){
const hi=await sayHi();
const myHi=await new Promise((resolve,reject)=>{
reject("error")
}).then((value)=>{
return "hello,world";
},(error)=>{
return value
})
console.log(hi);
console.log(myHi) //输出:error
}
test()
我们可以看到,此时类返回值是Promise.reject(value)中的返回值,如果没有返回值,则此时的await返回值是undefined。
不过我们经常在使用Promise对象的时候不会去指定Proise.reject的回调函数。例如:
async function sayHi(){
return "hi,world";
}
async function test(){
const hi=await sayHi();
const myHi=await new Promise((resolve,reject)=>{
reject("error")
}).then((value)=>{
return "hello,world";
}) //没有指定Promise.reject的回调函数
console.log(hi);
console.log(myHi)
}
test()
此时程序就会出错,报uncaught exception: error的错误。所以我们为了避免这种情况的发生,我们应该把await修饰的Promise放到try{}.catch()中。例如:
async function sayHi(){
return "hi,world";
}
async function test(){
const hi=await sayHi();
try{
var myHi=await new Promise((resolve,reject)=>{ //把const修改成var关键词
reject("error")
}).then((value)=>{
return "hello,world";
})
}catch(e) {
myHi=e;
}
console.log(hi);
console.log(myHi) //输出:error
}
test()
async和await大显身手
当Promise需要多层调用Promise.then()的时候,就能体现async和await的优点了。
function add(num){
return num+200;
}
async function test(){
const step1=await add(100);
const step2=await add(step1);
const step3=await add(step2);
console.log(step3)
}
function thenTest(){
const result=new Promise( (resolve,reject)=> {
resolve(100)
}).then((value)=>{
return add(value)
}).then((value)=>{
return add(value)
}).then((value)=>{
console.log(add(value))
})
}
test() //输出:700 (100+200+200+200)
thenTest() //输出:700 (100+200+200+200)
我们可以看到async和await可以避免Promise的多次then()函数的调用,让语法更加清晰。
我是大麦,如果喜欢我的文章,请给我一颗小心心。