前端知识点

JS | async和await

2019-05-05  本文已影响0人  灬劣徒

async和await的定义

任何一个名称都是有含义的,先从字面意思来理解.async是"异步"的简写,而await可以认为是async wait的简写,于是我们可以理解:

async起什么作用

这个问题的关键在于,async函数是如何处理它的返回值的!
我们当然希望它可以直接通过return语句返回我们想要的结果,可如果是这样,似乎就没await什么事儿了.所以让我们写段代码来试试,看它到底会返回什么

async function testAsync() {
  return 'hello';
}
const result = testAsync();
console.log(result)  // promise{<resolved>:'hello'};

上面代码显示,最后输出的是一个promise对象.所以 async函数返回的是一个Promise对象,从 文档 中也可以得到这个信息.async函数(包括函数语句,函数表达式,Lambda表达式)会返回一个Promise对象.如果函数体中return一个直接量,async会把这个直接量通过Promise.resolve()封装成一个Promise对象.

async函数返回的是一个Promise对象,所以在最外层不能用await获取其返回值的情况下,我们当然应该用原来的方式:then()链来处理这个Promise对象,就像这样

testAsync().then( v => {
  console.log(v) // hello
})

现在回过头来想下,如果async函数没有返回值,又改如何?很容易想到,它会返回
Promise.resolve(undefined)

联想一下Promise的特点 --- 无等待,所以在没有await的情况下执行async函数,它会立即执行,返回一个Promise对象,并且,绝不会阻塞后面的进程,这和普通返回Promise对象的函数并无不同

那么下一个关键点就在于await关键字了

await到底在等啥

一般来说,都认为await是在等待一个async函数完成.不过按照语法说明,await等待的是一个表达式,这个表达式的计算结果是一个Promise对象或其他说明值(换句话说,就是没有限定)

因为async返回的是一个Promise对象,所以await可以用于等待一个async函数的返回值---这也可以说是await在等async函数,但要清楚,它等的其实是一个返回值.注意await不仅仅用于等待Promise对象,它可以等待任意表达式的返回值,所以,await后面实际是可以接普通函数调用或者直接量的.所以下面这个示例是可以正确运行的

async function getSomething() {
  return 'getSomething';
}

async function testAsync() {
  return Promise.resolve('hello');
}

async function test() {
  const v1 = getSomething();
  const v2 = testAsync();
  console.log(v1,v2)
}

test();

await等到了要等的,然后呢

await等到了它要等的东西,一个Promise对象,或者其他值,然后呢?我不得不先说,await是一个运算符,用于组成表达式,await表达式的运算结果取决于它等的东西

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

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

image.png

async和await帮我们干了啥

作个简单的比较

上面已经说明了,async会将后面的函数()的返回值封装成一个Promise,而await会等待这个Promise完成,并将resolve的值返回出来

现在举例,用setTimeout模拟耗时的异步操作,先来看看不用async和await会怎么写

function takelongTime() {
  return new Promise( resolve => {
    setTimeout(() = > {
      console.log('loong_time_value')
    },1000)
  })
}

takeLongTime().then( v => {
  console.log(v)
})

现在改用async和await以后, 会是这样:

function takeLongTime() {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('long_time_value');
    })  
  })
}

async function test() {
  const v = await takeLongTime();
  console.log(v);
}

test();

这里takeLongTime函数没有用async申明.实际上,takeLongTime本身就是返回的Promise对象,加不加async结果都一样.

上面两种方式,都是对异步调用的处理(实际都是对promise对象的处理)差别并不明显,甚至使用async和await,代码看起来更多,那么它的优势到底在哪里?

async和await的优势在于处理then链

单一的Promise链并不能发现async和await的优势,它的优势在于处理由多个Promise组成的then链

假设一个业务,分多个步骤完成,每一个步骤依赖上一个步骤的结果,我们依然用setTimeout来模拟异步操作

/**
  *传入参数n,表示这个函数需要执行的时间(毫秒)
  *得到的结果是n + 200,这个值将用于下一步骤
  */
function takeLongTime() {
  return new Promise(resolve => {
    setTimeout(() => resolve(n + 200),n)
  })
}
function stemp1() {
  console.log(`stemp1 with ${n}`)
  return takeLongTime(n);
}
function stemp2() {
  console.log(`stemp2 with ${n}`)
  return takeLongTime(n);
}
function stemp3() {
  console.log(`stemp3 with ${n}`)
  return takeLongTime(n);
}

现在用promise方法来实现这三个步骤的处理

function doIt() {
  console.time('doIt');
  const time1 = 300;
  stemp1(time1)
         .then(time2 => stemp2(time2))
         .then(time3 => stemp3(time3))
         .then(result => {
            console.log(`result is ${result}`)
            console.timeEnd('doIt')
          }) 
}
doIt();
输出结果 result 是 step3() 的参数 700 + 200 = 900。doIt() 顺序执行了三个步骤,
一共用了 300 + 500 + 700 = 1500 毫秒,和 console.time()/console.timeEnd() 计算的结果基本一致。

下面用async和await来实现

async function doIt() {
  console.log('doIt');
  const time1 = 300;
  const time2 = await stemp1(time1);
  const time3 = await stemp2(time2);
  const result = await stemp3(time3);
  console.log(`result is ${result}`)
  console.timeEnd('doIt');
}

结果和之前的promsise实现是一致的,这样做的好处就是代码清晰了很多,几乎跟同步代码是一样的
原文摘自 《理解JavaScript 的 async/await》

上一篇下一篇

猜你喜欢

热点阅读