10. Async异步函数

2/20/2022 ES6

# 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) reject(error)
      else resolve(data)
    })
  })
}

// then调用
readFile('/etc/fstab').then(f1 => {
  readFile('/etc/shells').then(f2 => {
    console.log(f1, f2)
  })
})
// genarator调用 
function* gen() {
  const f1 = yield readFile('/etc/fstab')
  const f2 = yield readFile('/etc/shells')
  console.log(f1.toString(),f2.toString())
}
let f = gen()
f.next() // 得到f1
f.next() // 得到f2
f.next() // 输出结果

上述代码修改成async函数

async function asyncReadFile (){
  const f1 = await readFile('/etc/fstab') // 得到f1
  const f2 = await readFile('/etc/shells') // 得到f2
  console.log(f1.toString(),f2.toString()) // 输出结果
}
asyncReadFile()

# 2. Async函数基本用法

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
console.log('哈哈') // 先打印

# 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 (opens new window)会更清楚这个过程)

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 = +new Date
    setTimeout(() => resolve(+new Date - 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. 良好实践

多次尝试获取数据

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
}
    要么重构,
    要么享受!
    红莲华
    x
    loading...