ES6的学习笔记(十一)Async函数

176 阅读4分钟

Async函数

1.含义

ES2017引入Async函数,作为Generator函数的语法糖,使得异步操作更方便。

  • Async函数的执行就像普通函数一样,调用即可执行。例: asyncFunc();
  • Async函数具有较好的语义,async表示函数内有异步操作,await表示其后的表达式需要等待结果。
  • await关键字后可以是Promise对象,也可是原始类型的值(数值、字符串、布尔值,,但会转换成立即resolved的Promise对象。)
  • async函数返回的是Promise对象,因此可以使用链式编程。 总结,async函数可以看成由多个异步操作包装成的一个Promise对象,await命令就是内部then命令的语法糖。

2.基本用法

async函数返回一个Promise对象,可以使用then方法添加回调函数。当async函数执行的时候,遇到await就先返回,等到其后的异步操作完成后,再继续执行函数体内后面的语句。
async函数有多种使用形式。

// 函数声明形式
async function foo(){}
// 函数表达式 
let foo = async function (){}
// 作为对象的方法
let obj = {
	async foo(){}
}
// Class 的方法
class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
    const cache = await this.cachePromise;
    return cache.match(`/avatars/${name}.jpg`);
  }
}
// 箭头函数
let foo = async ()=> {}

3.语法

返回Promise对象。

  • async函数返回一个Promise对象。
  • async函数内部return的值,会成为then方法回调函数的参数。
async function foo() {
    return 'Hello async!'
}
foo().then(res => {
    console.log(res) // Hello async!
})

async函数内部抛出错误,会导致返回的Promise对象变为rejected状态。而抛出的错误对象会被catch方法的回调函数接收到。

 async function foo() {
       throw new Error('error')
   }
//    foo().then((res) => {
//        console.log(res)
//    },(err) => {
//        console.log(err) // Error: error
//    })
foo().then((result) => {
    console.log(result)
}).catch((err) => {
    console.log(err)
})

Promise对象的状态变化

async函数内部所有await命令后的Promise对象执行完后,async函数返回的Promise对象的状态才会发生改变,除非函数内部return或抛出错误。
即,async函数内部所有的异步操作执行完后才会执行then方法中的回调函数。

// 将就学习案例
async function getTitle(url) {
  let response = await fetch(url);
  let html = await response.text();
  return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
// "ECMAScript 2017 Language Specification"

await命令

正常情况,await后面是Promise对象,返回该对象的结果。如果不是Promise对象,则直接返回对应的值。

async function foo(){
  // return 123
  return await 123
}
foo().then((params) => {
  console.log(params) // 123
})

await命令后面是一个thenable对象(即定义了then方法的对象),那么await会将其等同于 Promise 对象。

class Sleep {
  constructor(timeout) {
    this.timeout = timeout;
  }
  then(resolve, reject) {
    const startTime = Date.now();
    setTimeout(
      () => resolve(Date.now() - startTime),
      this.timeout
    );
  }
}

(async () => {
  const sleepTime = await new Sleep(1000);
  console.log(sleepTime);
})();
// 1000
/*上面代码中,await命令后面是一个Sleep对象的实例。这个实例不是 Promise 对象,但是因为定义了then方法,await会将其视为Promise处理。
这个例子还演示了如何实现休眠效果。JavaScript 一直没有休眠的语法,但是借助await命令就可以让程序停顿指定的时间。下面给出了一个简化的sleep实现。*/
function sleep(interval) {
  return new Promise(resolve => {
    setTimeout(resolve, interval);
  })
}

// 用法
async function one2FiveInAsync() {
  for(let i = 1; i <= 5; i++) {
    console.log(i);
    await sleep(1000);
  }
}

one2FiveInAsync();

await命令后的Promise对象若变为reject状态,则reject抛出的值会被catch方法的回调函数接收到。

async function foo() {
    await Promise.reject('error')
}
foo().then((res) => {

}).catch((err) => {
    console.log(err) // error
})
// await语句前面没有return。加上return语句效果也是一样的。

任何一个await语句后的Promise对象变为reject状态,都会中断async函数的执行。

async function foo() {
    await Promise.reject('error')
    await Promise.resolve('value') // 这句不会执行。
}

使用try...catch,即使前面的异步操作失败,也不会中断async函数的执行。或者在前面的异步操作后使用catch捕捉前面产生的错误。

async function foo() {
  try {
    await Promise.reject('Error')
  } catch (error) {

  }
  return await Promise.resolve('Value')
  }
  foo().then(res => {
  console.log(res) // Value
  })
  
async function foo() {

await Promise.reject('Error').catch(err => {
    console.log(err)
})

return await Promise.resolve('Value')
}
foo().then(res => {
console.log(res) 
})
// Error
// Value

错误处理

await命令后的异步操作出错,即等同于async函数返回的Promise被reject。

async function foo() {
    return new Promise((resolve,reject) => {
        throw new Error('error')
    })
}
foo().then((res) => {

}).catch((err) => {
    console.log(err)  // Error:error
})

防止出错的方法,将其放入try...catch语句之中。

async function foo() {
    try {
        await new Promise((resolve,reject) => {
            throw new Error('error')
        })
    } catch (error) {
        // dosomething
    }
}

多个await命令,可以统一放入try...catch之中

// 原例子
async function main() {
  try {
    const val1 = await firstStep();
    const val2 = await secondStep(val1);
    const val3 = await thirdStep(val1, val2);

    console.log('Final: ', val3);
  }
  catch (err) {
    console.error(err);
  }
}

使用注意点 (未完。。。)

  • 最好将await命令放入try...catch之中。