10.Async异步函数

1.Async函数介绍

asnyc函数实际上是Generator+Promise的语法糖

const fs = require('fs')

function readFile(fileName) {
  return new Promise(function (resolve, reject) {
    fs.readFile(fileName, function(error, data) {
      if (error) return reject(error)
      resolve(data)
    })
  })
}

function* gen() {
  const f1 = yield readFile('/etc/fstab')
  const f2 = yield readFile('/etc/shells')
  console.log(f1.toString())
  console.log(f2.toString())
}
let f = gen()
f.next()
f.next()
f.next()

上述代码修改成async函数

async function asyncReadFile (){
  const f1 = await  readFile('/etc/fstab')
  const f2 = await  readFile('/etc/shells')
  console.log(f1.toString())
  console.log(f2.toString())
}
asyncReadFile()
2.基本用法

async函数返回一个Promise对象,可以使用then方法进行回调。当函数执行遇到await就会先返回,等异步操作完成后,在接着执行函数体后面的语句

async function getStockPriceByName(name) {
  const symbol = await getStockSymbol(name)
  const stockPrice = await getStockPrice(symbol)
  return stockPrice
}

getStockPriceByName('goog').then(function (result) {
  console.log(result)
})
function timeout(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms) // ms秒之后resolve
  });
}

async function asyncPrint(value, ms) {
  await timeout(ms)
  console.log(value)
}

asyncPrint('hello world', 50) // 在50ms后才会输出hello world
3.async声明方式

// 函数声明
async function fn() {}

// 函数表达式
const fn = async function () {}

// 箭头函数
const fn = async () => {}

// 对象方法
let obj = { async fn() {} }

// Class的方法
class Storage {
  constructor() { ... }
  async fn() { ... }
}
4.async本质上是Promise

async函数结束return的参数会封装成为一个Promise,状态为fulfilled,因为函数本身就是一个Promise

async function fn(){
  return 'data'
}
fn().then(res => console.log(res)) // data

同理,throw抛出错误会返回一个Promise,状态为rejected

async function fn(){
  throw new Error('出错了')
}
fn().then{
  res => console.log('resolve: ' + res),
  err => console.log('reject: ' + err)
}
// reject Error: 出错了

返回的Promise对象,必须等到await命令后面的Promise对象执行完,才会发生状态改变,意味着必须等到async函数异步操作执行完才会执行then(看有关Generator函数和Promise结合模拟实现async/await会更清楚这个过程)

async function getTitle(url) {
  let res = await fetch(url)
  let html = await res.text()
  return html.match(/<title>([\s\S]+)<\/title>/i)[1]
}
getTitle('https://www.baidu.com').then(res => console.log(res))
// 抓取网页、取出文本、匹配页面标题。只有这三个操作全部完成,才会执行then方法里面的console.log
5.await命令后面若是thenable对象,会将其视为Promise处理,调用其then方法并接收其resolve函数的参数
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(3000)
  console.log(sleepTime);
})()
// 3000
6.await命令后面的Promise对象处于reject状态时,会立即中断执行,并抛错传入catch回调函数
async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
}
f()
.then(v => console.log(v)) // 不执行
.catch(e => console.log(e)) // 出错了

若不希望中断,那么就要把其放入try...catch

async function f() {
  try{
    await Promise.reject('出错了');
  }catch(e){
    console.log(e) // 出错了
  }
  await Promise.resolve('hello world'); // 继续执行
}
f()
.then(v => console.log(v)) // hello world

或者及时在await后面的Promise对象调用catch方法处理错误

async function f() {
  await Promise.reject('出错了').catch(e => console.log(e))
  return await Promise.resolve('hello world'); // 继续执行
}
f().then(v => console.log(v))
// 出错了
// hello world
7.良好实践

如果await后面的异步操作出错,那么等同于async函数返回的Promise 对象被reject

async function f() {
  await new Promise(function (resolve, reject) {
    throw new Error('出错了')
  })
}

f()
.then(v => console.log(v))
.catch(e => console.log(e)) // Error:出错了

防止出错方法,将其放入try...catch

async function f() {
  try {
    await new Promise(function (resolve, reject) {
      throw new Error('出错了')
    })
  } catch(e) {// ...在这里处理错误}
  return await('hello world')
}
async function main() {
  try {
    const val1 = await firstStep()
    const val2 = await secondStep(val1)
    const val3 = await thirdStep(val1, val2)
    return val3
  }
  catch (e) {
    console.error(e)
  }
}

多次尝试获取数据

const superagent = require('superagent')
const NUM_RETRIES = 3

async function test() {
  let i
  for (i = 0; i < NUM_RETRIES; ++i) {
    try {
      await superagent.get('http://google.com/this-throws-an-error')
      break
    } catch(err) {}
  }
  console.log(i)
}
test() // 3
// 证明请求都是无效的,因为只要成功,就会立刻break跳出循环

将继发改为并行触发(很重要)

async function fn(){
  let foo = await getFoo()
  let bar = await getBar()
  // 这里bar要等foo获得数据后才会执行,是继发的关系,
  // 但是,这样很费事,两个应该是并行触发的才对

  // 改进方法
  let [foo, bar] = await Promise.all([getFoo(), getBar()])
  // 或者
  let fooPromise = getFoo()
  let barPromise = getBar()
  let foo = await fooPromise
  let bar = await barPromise
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值