【学习笔记】深刻理解async/await

参考资料:理解javaScript中的async/await,感谢原文做者的总结,本文在理解的基础上作了一点小小的修改,主要为了加深本身的知识点掌握java

学完了Promise,咱们知道能够用then链来解决多层回调问题,可是这还不是最理想的操做,咱们须要调用不少个then链才能达到要求,那么有没有一种更简便代码量更少的方式达到then链相同的结果呢?asynv和await就很好地解决了这个问题,首先用async声明一个异步函数,而后再用await等待异步结果,把之前then链的结果放到直接放在await,很是方便。segmentfault

那么,async和await原理是什么呢?为何能够用这样的语法来优化then链呢?异步

1. async/await是什么?

async/await实际上是Promise的语法糖,它能实现的效果都能用then链来实现,这也和咱们以前提到的同样,它是为优化then链而开发出来的。从字面上来看,async是“异步”的简写,await译为等待,因此咱们很好理解async声明function是异步的,await等待某个操做完成。固然语法上强制规定await只能出如今asnyc函数中,咱们先来看看async函数返回了什么: async

async function testAsy(){
   return 'hello world';
}
let result = testAsy(); 
console.log(result)

这个async声明的异步函数把return后面直接量经过Promise.resolve()返回Promise对象,因此若是这个最外层没有用await调用的话,是能够用原来then链的方式来调用的:函数

async function testAsy(){
   return 'hello world'
}
let result = testAsy() 
console.log(result)
result.then(v=>{
    console.log(v)   //hello world
})

联想一下Promise特色——异步无等待,因此当没有await语句执行async函数,它就会当即执行,返回一个Promise对象,非阻塞,与普通的Promise对象函数一致。优化

重点就在await,它等待什么呢?spa

按照语法说明,await等待的是一个Promise对象,或者是其余值(也就是说能够等待任何值),若是等待的是Promise对象,则返回Promise的处理结果;若是是其余值,则返回该值自己。而且await会暂停当前async function的执行,等待Promise的处理完成。若Promise正常处理(fulfillded),其将回调的resolve函数参数做为await表达式的值,继续执行async function;若Promise处理异常(rejected),await表达式会把Promise异常缘由抛出;另外若是await操做符后面的表达式不是一个Promise对象,则返回该值自己。code

2. 深刻理解async/await

咱们来详细说明一下async/await的做用。await操做符后面能够是任意值,当是Promise对象的时候,会暂停async function执行。也就是说,必须得等待await后面的Promise处理完成才能继续:对象

 function testAsy(x){
   return new Promise(resolve=>{setTimeout(() => {
       resolve(x);
     }, 3000)
    }
   )
}
async function testAwt(){    
  let result =  await testAsy('hello world');
  console.log(result);    // 3秒钟以后出现hello world
}
testAwt();

await 表达式的运算结果取决于它等的东西。blog

若是它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。

若是它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,而后获得 resolve 的值,做为 await 表达式的运算结果。

咱们再把上面的代码修改一下,好好体会“阻塞”这个词

 function testAsy(x){
   return new Promise(resolve=>{setTimeout(() => {
       resolve(x);
     }, 3000)
    }
   )
}
async function testAwt(){    
  let result =  await testAsy('hello world');
  console.log(result);    // 3秒钟以后出现hello world
  console.log('tangj')   // 3秒钟以后出现tangj
}
testAwt();
console.log('tangSir')  //当即输出tangSir

这就是 await 必须用在 async 函数中的缘由。async 函数调用不会形成阻塞,它内部全部的阻塞都被封装在一个 Promise 对象中异步执行。await暂停当前async的执行,因此'tangSir''最早输出,hello world'和‘tangj’是3秒钟后同时出现的。

3. async和await简单应用

上面已经说明了 async 会将其后的函数(函数表达式或 Lambda)的返回值封装成一个 Promise 对象,而 await 会等待这个 Promise 完成,并将其 resolve 的结果返回出来。

如今举例,用 setTimeout模拟耗时的异步操做,先来看看不用 async/await 会怎么写

function takeLongTime() {
    return new Promise(resolve => {
        setTimeout(() => resolve("long_time_value"), 1000);
    });
}

takeLongTime().then(v => {
    console.log("got", v); //一秒钟后输出got long_time_value
});

若是改用 async/await 呢,会是这样

function takeLongTime() {
    return new Promise(resolve => {
        setTimeout(() => resolve("long_time_value"), 1000);
    });
}

async function test() {
    const v = await takeLongTime();
    console.log(v);  // 一秒钟后输出long_time_value
}

test();

tankLongTime()自己就是返回的 Promise 对象,因此加不加 async结果都同样。

4. 处理then链

前面咱们说了,async和await是处理then链的语法糖,如今咱们来看看具体是怎么实现的:

假设一个业务,分多个步骤完成,每一个步骤都是异步的,并且依赖于上一个步骤的结果。咱们仍然用setTimeout来模拟异步操做:

/**
 * 传入参数 n,表示这个函数执行的时间(毫秒)
 * 执行的结果是 n + 200,这个值将用于下一步骤
 */
function takeLongTime(n) {
    return new Promise(resolve => {
        setTimeout(() => resolve(n + 200), n);
    });
}

function step1(n) {
    console.log(`step1 with ${n}`);
    return takeLongTime(n);
}

function step2(n) {
    console.log(`step2 with ${n}`);
    return takeLongTime(n);
}

function step3(n) {
    console.log(`step3 with ${n}`);
    return takeLongTime(n);
}

如今用 Promise 方式来实现这三个步骤的处理。

function doIt(){
    console.time('doIt');
    let time1 = 300;
    step1(time1)
        .then((time2) => step2(time2))
        .then((time3) => step3(time3))  
        .then((result) => {
            console.log(`result is ${result}`);
            console.timeEnd("doIt");
        })
}

doIt();

//执行结果为:
//step1 with 300
//step2 with 500
//step3 with 700
//result is 900
//doIt: 1510.2490234375ms

输出结果 result 是 step3() 的参数 700 + 200 = 900doIt() 顺序执行了三个步骤,一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 计算的结果一致。

若是用 async/await 来实现呢,会是这样:

async function doIt() {
    console.time('doIt');
    let time1 = 300;
    let time2 = await step1(time1);//将Promise对象resolve(n+200)的值赋给time2
    let time3 = await step1(time2);
    let result = await step1(time3);
    console.log(`result is ${result}`);
    console.timeEnd('doIt');
}

doIt();

//执行结果为:
//step1 with 300
//step2 with 500
//step3 with 700
//result is 900
//doIt: 1512.904296875ms

显然咱们用async/await简单多了。

5. Promise处理结果为rejected

await 命令后面的 Promise 对象,运行结果多是 rejected,因此最好把 await 命令放在 try...catch 代码块中。

async function myFunction() {
    try {
        await somethingThatReturnAPromise();
    } catch (err){
        console.log(err);
    }
}

//另外一种写法
async function myFunction() {
    await somethingThatReturnAPromise().catch(function(err) {
        console.log(err);
    })
}
相关文章
相关标签/搜索