javascript进阶知识23 - async与await

176 阅读3分钟

async与await其实就是将Promise与它的链式调用简化,使用起来更简洁。

async

使用async时,必须将它放在function关键字前面。

async function fn() {
    console.log('1')
}
fn();  // '1'

async函数是同步执行的

此外,async虽然是异步的意思,但是这个函数是同步执行的,不是异步执行的。

async function fn() {
    console.log('1')
}
console.log('2');
fn();
console.log('3')

image.png

async函数有返回值

async函数默认就有返回值。

async function fn() {
    console.log('1')
}
let result = fn();
console.log(result);

image.png

我们发现,它默认返回的是一个Promise,且状态为<fullfilled>,PromiseResult为undefined.

如果我们自己返回一个值呢?

async function fn() {
    return 1;
}
let result = fn();
console.log(result);

image.png

我们发现,他还是返回的Promise实例,但是PromiseResult的值变为了我们返回的值。

//.....接上面的代码
console.log(typeof result) // 'object'  类型为对象 

await

await 可以认为是 async wait 的简写,所以await 用于等待一个异步方法执行完成。

await 只能出现在 async 函数中

function fn() {
    let a = await 'await';
    console.log(a);
}
fn();

image.png

我们发现,报错的信息是await 仅仅在async function中是有效的

async function fn() {
    let a = await 'await';
    console.log(a);
}
fn();  // 'await'

其实await的原理就是使用.then链式调用。

原理:

// 使用promise.then
new Promise((res,err) => {
    res('yes')
}).then(data => {
    console.log(data);  // 'yes'
})
// 改一下方式
let p = new Promise((res,err) => {
    res('yes')
})
p.then(data => {
    console.log(data)  // 'yes'
})

await就是类似第二种。

let p = new Promise((res,err) => {
    res('yes')
});
async function fn() {
    let data = await p;
    console.log(data);  // 'yes'
    console.log(typeof data);   // string
}
fn();

所以我们可以得出,await后面跟的是一个promise实例,而返回的结果就是这个promise实例的PromiseResult而不是一个实例对象(和then的区别)。

如果我们await后面跟的不是promise实例,那么js会自动帮我们把它转换为resolve()后的Promise实例。

async function fn() {
    let data = await 'yes';
    console.log(data);  // 'yes'
    console.log(typeof data);   // string
}
fn();

await是异步的

从上面我们得出,await就是.then的语法糖,本质其实就是.then,而在promise那一节我们也得出了,then属于一种异步任务,所以await也属于异步的。

async function fn() {
    let data = await 'yes';
    console.log(data);  // 'yes'
    console.log(typeof data);   // string
}
fn();
console.log('hahha');

image.png

我们可以看出,浏览器会先执行后面的代码(先输出‘haha’),在执行await后面的代码。

await的缺点

其实我们了解了await的运行原理后,我们也就懂了await其实就是让异步代码在async函数里面同步执行,所以这也就会导致一个问题,在async函数里面await后面代码会先等待await执行完毕,在执行await后面的代码,但是await又属于异步的,浏览器又会将异步代码放在一个队列中,等待同步代码执行完毕后,再执行队里里的异步代码。所以,await后面的代码,就会变成‘异步’的了,我想这也就是为啥函数前面要加一个async的原因吧!

async function asy() {
   return new Promise((resolve,reject) => {
       setTimeout(()=>{
           resolve('yes')
       },2000)
   })
};

async function fn() {
    let data = await asy();
    console.log(data); 
    console.log('yeyeye');
}
fn();
console.log('hahha');

image.png

我们发现,asy函数要等待2s后才resolve,这也就会让await在2s后才会执行,这也就让await后面的代码也会等待2s后再执行!

async与await处理错误

await不止会返回<fullfilled>状态的promise实例,还可能会返回rejected状态的promise实例。而且如果返回了一个错误,这就会导致我们的程序中断,浏览器不会继续执行后面的代码,那么我们如何处理await返回的错误呢?

async function asy() {
   return new Promise((resolve,reject) => {
       setTimeout(()=>{
           reject('no')
       },100)
   })
};

async function fn() {
    let data = await asy();
    console.log(data);
    console.log('aaa');

}
fn();

image.png

使用try/catch处理错误

async function asy() {
   return new Promise((resolve,reject) => {
       setTimeout(()=>{
           reject('no')
       },100)
   })
};

async function fn() {
    try{
        let data = await asy();
        console.log(data); 
        console.log('aaa')
    } catch(err){
        console.log(err)
    }
}
fn();

image.png

这也就不会导致报错了!

使用.catch()进行处理

还有一种处理错误的方式是使用.catch的方式:

async function asy() {
   return new Promise((resolve,reject) => {
       setTimeout(()=>{
           reject('no')
       },100)
   })
};

async function fn() {
    let data = await asy();
    console.log(data);
    console.log('aaa');
}
fn().catch((err) => {
    console.log(err)
});

image.png

但是我一般用的都是第一种 try/catch的方式。


耶,js要写完了!!