Async/Await 边学边练

Async/Await是ES2017中加入的新特性,解救众生于Promise

这篇文章主要根据Async/Await的一个介绍视频,截图供大家入门学习

1 发展历史

callback最受诟病的就是嵌套的括号,不美观,错误处理也复杂

Promise就好很多,结构清晰,统一错误处理

主角Async/Await登场了,传统的函数调用方式,线性编程方式

示例

注意:下面的示例需要在node>=7.6的环境下,支持async/wait

  • 1-sequential

 1-1-naive

 记录两个方法运行的时间,总共运行的时间

 processes.js

const util = require('util')
const wait = util.promisify(setTimeout)

module.exports = {
  async process01 () {
    console.log('Process 01 started')
    console.time('Process 01 ended')
    await wait(5000)
    console.timeEnd('Process 01 ended')
    console.log()
    return 'process01-value'
  },

  async process02 () {
    console.log('Process 02 started')
    console.time('Process 02 ended')
    await wait(3000)
    console.timeEnd('Process 02 ended')
    console.log()
    return 'process02-value'
  }
}

第一行的util是nodejs自带的工具类,本机需要安装node

第二行util.promisify() 用于将那些接受回调函数的函数,转变为 Promise

main.js

const {process01, process02} = require('./processes')

async function main () {
  try {
    console.time('Total Running Time')
    const value01 = await process01()
    const value02 = await process02()

    console.log('Process 01 Returned: ', value01)
    console.log('Process 02 Returned: ', value02)

    console.log()

    console.timeEnd('Total Running Time')
  } catch (error) {
    console.error('error', error)
  }
}

main()

结果

  

1-2-issue

异常处理,如果被调用函数没有处理异常,则会中断执行调用函数

processes.js

const util = require('util')
const wait = util.promisify(setTimeout)

module.exports = {
  async process01 () {
    console.log('Process 01 started')
    throw new Error('Process 01 Failed')
    console.time('Process 01 ended')
    await wait(5000)
    console.timeEnd('Process 01 ended')
    console.log()
    return 'process01-value'
  },

  async process02 () {
    console.log('Process 02 started')
    console.time('Process 02 ended')
    await wait(3000)
    console.timeEnd('Process 02 ended')
    console.log()
    return 'process02-value'
  }
}

main.js

const {process01, process02} = require('./processes')

async function main () {
  try {
    console.time('Total Running Time')
    const value01 = await process01()
    const value02 = await process02()

    console.log('Process 01 Returned: ', value01)
    console.log('Process 02 Returned: ', value02)

    console.log()

    console.timeEnd('Total Running Time')
  } catch (error) {
    console.error('error', error)
  }
}

main()

结果

1-3-solution

针对1-2-issue的错误处理,在process01中增加了异常处理,不会中断调用函数的执行

processes.js

const util = require('util')
const wait = util.promisify(setTimeout)

module.exports = {
  async process01 () {
    try {
      console.log('Process 01 started')
      throw new Error('Process 01 Failed')
      console.time('Process 01 ended')
      await wait(5000)
      console.timeEnd('Process 01 ended')
      console.log()
      return 'process01-value'
    } catch (error) {
      console.error(error)
    }
  },

  async process02 () {
    try {
      console.log('Process 02 started')
      console.time('Process 02 ended')
      await wait(3000)
      console.timeEnd('Process 02 ended')
      console.log()
      return 'process02-value'
    } catch (error) {
      console.error(error)
    }
  }
}

               

main.js

const {process01, process02} = require('./processes')

async function main () {
  try {
    console.time('Total Running Time')
    const value01 = await process01()
    const value02 = await process02()

    console.log('Process 01 Returned: ', value01)
    console.log('Process 02 Returned: ', value02)

    console.log()

    console.timeEnd('Total Running Time')
  } catch (error) {
    console.error('error', error)
  }
}

main()

结果

  • 02-parallel

2-1-naive

并行执行异步函数

processes.js

const util = require('util')
const wait = util.promisify(setTimeout)

module.exports = {
  async process01 () {
    console.log('Process 01 started')
    console.time('Process 01 ended')
    await wait(5000)
    console.timeEnd('Process 01 ended')
    console.log()
    return 'process01-value'
  },

  async process02 () {
    console.log('Process 02 started')
    console.time('Process 02 ended')
    await wait(3000)
    console.timeEnd('Process 02 ended')
    console.log()
    return 'process02-value'
  }
}

main.js

const {process01, process02} = require('./processes')

async function main () {
  try {
    console.time('Total Running Time')
    const data = await Promise.all([process01(), process02()]) // 并行执行

    console.log()

    console.log('Process 01 Returned: ', data[0])
    console.log('Process 02 Returned: ', data[1])

    console.log()

    console.timeEnd('Total Running Time')
  } catch (error) {
    console.error('error', error)
  }
}

main()

结果

2-2-issue

并行执行2个函数,其中一个异步函数抛异常,不影响另一个函数的执行,但影响返回之后的操作

processes.js

const util = require('util')
const wait = util.promisify(setTimeout)

module.exports = {
  async process01 () {
    console.log('Process 01 started')
    throw new Error('Process 01 Failed')
    console.time('Process 01 ended')
    await wait(5000)
    console.timeEnd('Process 01 ended')
    console.log()
    return 'process01-value'
  },

  async process02 () {
    console.log('Process 02 started')
    console.time('Process 02 ended')
    await wait(3000)
    console.timeEnd('Process 02 ended')
    console.log()
    return 'process02-value'
  }
}

main.js

const {process01, process02} = require('./processes')

async function main () {
  try {
    console.time('Total Running Time')
    const data = await Promise.all([process01(), process02()]) // 并行执行

    console.log()

    console.log('Process 01 Returned: ', data[0])
    console.log('Process 02 Returned: ', data[1])

    console.log()

    console.timeEnd('Total Running Time')
  } catch (error) {
    console.error('error', error)
  }
}

main()

结果

2-3-solution

被调用的异步函数添加异常处理,同样可以返回执行结果,不影响调用函数的继续执行

processes.js

const util = require('util')
const wait = util.promisify(setTimeout)

module.exports = {
  async process01 () {
    try {
      console.log('Process 01 started')
      throw new Error('Process 01 Failed')
      console.time('Process 01 ended')
      await wait(5000)
      console.timeEnd('Process 01 ended')
      console.log()
      return 'process01-value'
    } catch (error) {
      console.error(error)
    }
  },

  async process02 () {
    try {
      console.log('Process 02 started')
      console.time('Process 02 ended')
      await wait(3000)
      console.timeEnd('Process 02 ended')
      console.log()
      return 'process02-value'
    } catch (error) {
      console.error(error)
    }
  }
}

main.js

const {process01, process02} = require('./processes')

async function main () {
  try {
    console.time('Total Running Time')
    const data = await Promise.all([process01(), process02()]) // 并行执行

    console.log()

    console.log('Process 01 Returned: ', data[0])
    console.log('Process 02 Returned: ', data[1])

    console.log()

    console.timeEnd('Total Running Time')
  } catch (error) {
    console.error('error', error)
  }
}

main()

结果

  • 03-loops

3-1-for-loop

某个方法的benchmark, 循环执行10次,计算总耗时和平均耗时

processes.js

const util = require('util')
const wait = util.promisify(setTimeout)

function getRandBetween (a, b) {
  return Math.floor(Math.random() * b) + a
}

module.exports = {
  async secretAlgorithm () {
    console.time('secretAlgorithm')
    await wait(getRandBetween(1, 7) * 100)
    console.timeEnd('secretAlgorithm')
  }
}

main.js

const now = require('performance-now')
const {secretAlgorithm} = require('./processes')

const NUMBER_OF_RUNS = 10

async function main () {
  try {
    let totalTime = 0
    for (let i = 0; i < NUMBER_OF_RUNS; i++) {
      const start = now()
      await secretAlgorithm()
      const end = now()
      totalTime += (end - start)
    }
    console.log()

    console.log('totalTime: ', totalTime)
    console.log('Number of retries: ', NUMBER_OF_RUNS)
    console.log('Average Running Time: ', (totalTime / NUMBER_OF_RUNS).toFixed(3))

  } catch (error) {
    console.error('error', error)
  }
}

main()

结果

3-2-forEach

植5棵树,打印每棵树开关的时间, forEach中不能使用await, 可以使用 for...of 替代

PowerPlant.js

const util = require('util')
const wait = util.promisify(setTimeout)

function getRandBetween (a, b) {
  return Math.floor(Math.random() * b) + a
}
class PowerPlant {
  constructor (id) {
    this.id = id
  }

  async turnOn () {
    console.log(`Turning On Power Plant ${this.id}`)
    console.time(`Power Plant ${this.id} turned on`)
    await wait(getRandBetween(1, 5) * 200)
    console.timeEnd(`Power Plant ${this.id} turned on`)
    console.log()
  }

  async turnOff () {
    console.log(`Turning Off Power Plant ${this.id}`)
    console.time(`Power Plant ${this.id} turned off`)
    await wait(getRandBetween(1, 3) * 200)
    console.timeEnd(`Power Plant ${this.id} turned off`)
    console.log()
  }
}

module.exports = PowerPlant

main.js

const PowerPlant = require('./PowerPlant')
let powerPlants = []

async function main () {
  try {
    powerPlants.push(new PowerPlant('01'))
    powerPlants.push(new PowerPlant('02'))
    powerPlants.push(new PowerPlant('03'))
    powerPlants.push(new PowerPlant('04'))
    powerPlants.push(new PowerPlant('05'))

    /* turn on all of them */
    powerPlants.forEach(powerPlant => {
      await powerPlant.turnOn()
    })

    /* turn off all of them */
    powerPlants.forEach(powerPlant => {
      await powerPlant.turnOff()
    })
  } catch (error) {
    console.error('error', error)
  }
}

main()

结果

3-3-for-of

processes.js

const util = require('util')
const wait = util.promisify(setTimeout)

function getRandBetween (a, b) {
  return Math.floor(Math.random() * b) + a
}
class PowerPlant {
  constructor (id) {
    this.id = id
  }

  async turnOn () {
    console.log(`Turning On Power Plant ${this.id}`)
    console.time(`Power Plant ${this.id} turned on`)
    await wait(getRandBetween(1, 5) * 200)
    console.timeEnd(`Power Plant ${this.id} turned on`)
    console.log()
  }

  async turnOff () {
    console.log(`Turning Off Power Plant ${this.id}`)
    console.time(`Power Plant ${this.id} turned off`)
    await wait(getRandBetween(1, 3) * 200)
    console.timeEnd(`Power Plant ${this.id} turned off`)
    console.log()
  }
}

module.exports = PowerPlant

main.js

const PowerPlant = require('./PowerPlant')
let powerPlants = []

async function main () {
  try {
    powerPlants.push(new PowerPlant('01'))
    powerPlants.push(new PowerPlant('02'))
    powerPlants.push(new PowerPlant('03'))
    powerPlants.push(new PowerPlant('04'))
    powerPlants.push(new PowerPlant('05'))

    /* turn on all of them */
    for(let powerPlant of powerPlants) {
      await powerPlant.turnOn()
    }

    /* turn off all of them */
    for(let powerPlant of powerPlants) {
      await powerPlant.turnOff()
    }
  } catch (error) {
    console.error('error', error)
  }
}

main()

结果

3-4-while

某个方法的benchmark, 计算循环执行10次的耗时和平均耗时

processes.js

const util = require('util')
const wait = util.promisify(setTimeout)

function getRandBetween (a, b) {
  return Math.floor(Math.random() * b) + a
}

module.exports = {
  async secretAlgorithm () {
    console.time('secretAlgorithm')
    await wait(getRandBetween(1, 7) * 100)
    console.timeEnd('secretAlgorithm')
  }
}

main.js

const now = require('performance-now')
const {secretAlgorithm} = require('./processes')

const NUMBER_OF_RUNS = 10

async function main () {
  try {
    let totalTime = 0
    let retries = 0
    while (retries < NUMBER_OF_RUNS) {
      const start = now()
      await secretAlgorithm()
      const end = now()
      totalTime += (end - start)
      retries++
    }
    console.log()

    console.log('totalTime: ', totalTime)
    console.log('Number of retries: ', NUMBER_OF_RUNS)
    console.log('Average Running Time: ', (totalTime / NUMBER_OF_RUNS).toFixed(3))

  } catch (error) {
    console.error('error', error)
  }
}

main()

结果

https://www.youtube.com/watch?v=f57IHEeDNcA

猜你喜欢

转载自my.oschina.net/u/2510955/blog/1558846
今日推荐