(译文)学习ES6非常棒的特性——Async / Await函数

try/catch

在使用Async/Await前,我们可能这样写:

const main = (paramsA, paramsB, paramsC, done) => {
  funcA(paramsA, (err, resA) => {
    if (err) return done(err)
    return funcB(paramsB, (err, resB) => {
      if (err) return done(err)
      funcC(paramsC, (err, resC) => {
        if (err) return done(err)
        // (╯°□°)╯︵ ┻━┻
        return done(null, { resA, resB, resC })
      })
    })
  })
}

采用了Async/Await后:

const main = async (paramsA, paramsB, paramsC) => {
  const resA = await funcA(paramsA)
  const resB = await funcB(paramsB)
  const resC = await funcC(paramsC)
  // (T.T)/
  return { resA, resB, resC }
}

然后你会发现,我们没有处理错误异常的情况,然后你可能会这么写:

const main = async (paramsA, paramsB, paramsC) => {
  let resA
  let resB
  let resC
  try {
    resA = await funcA(paramsA)
  } catch (error) {
    throw error
  }
  try {
    resB = await funcB(paramsB)
  } catch (error) {
    throw error
  }
  try {
    resC = await funcC(paramsC)
  } catch (error) {
    throw error
  }
  // (o.o;)
  return { resA, resB, resC }
}

可能你不需要单独处理每个错误,然后你就这么写:

const main = async (paramsA, paramsB, paramsC) =>
  try {
    const resA = await funcA(paramsA)
    const resB = await funcB(paramsB)
    const resC = await funcC(paramsC)
      // (^.^')
    return { resA, resB, resC }
  } catch (error) {
    throw error
  }
}

但是,很快滴,你发现代码报了这样一个错误:(注意如果你用了Node.js,可能对这个错误不陌生)

(node: xxx) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: y): Error: some sort of error
(node: xxx) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
// (-.-;)

然后你会想,我草,哪里的原因。然后你花了20多分钟,去找问题。问题很简单就是:
你抛出了一个异常,但是你没有捕捉它和处理它。

因为Async/Await函数返回的是一个Promise,所以我们想下怎么解决它:

const main = async (paramsA, paramsB, paramsC) => {
  try {
    const resA = await funcA(paramsA)
    const resB = await funcB(paramsB)
    const resC = await funcC(paramsC)
    return { resA, resB, resC }
  } catch (error) {
    // sure it's thrown, but who catches it??
    throw error
  }
}
// somewhere else...
main()
  .then(d => { // do things with the result })
  .catch(e => { // handle that error! })

在.catch方法里面捕捉错误。但如果我真的想单独处理某个错误呢:

const main = async (paramsA, paramsB, paramsC) => {
  const resA = await funcA(paramsA)
  const resB = await funcB(paramsB).catch(e => { // things unique to this error })
  const resC = await funcC(paramsC)
  return { resA, resB, resC }
}
// ... all we need is this `.catch` to handle all of them.
main()
  .then(d => { // do things with the result })
  .catch(e => { // handle all other errors!! })

用Mocha, Sinon, and Chai进行测试

测试Async / Await函数非常简单,首先,你只要记住这三件事:
1 不要把异步函数和promise混一起

const thing = (params, done) => {
  ApiCall(params, async (err, data) => {
    if (err) return done(err)
    const things = await OtherApiCall(data)
    return done(null, things)
  })
}

像上面这样,真的会崩溃的。
2 记住Async / Await返回了一个Promise
3 如果你在Mocha测试里面返回了promise,mocha测试会处理好它,你不用担心。

ok.开始:
我们有一个主文件main.js

// main.js
const main = async (paramsA, paramsB, paramsC) => {
  const resA = await apiA.create(paramsA)
  const resB = await apiB.delete(paramsB)
  const resC = await apiC.update(paramsC)
  return { resA, resB, resC }
}

然后我们的测试文件就这样写:

// test.js
const expect = require('chai').expect
const sinon = require('sinon')
const main = require('main.js')
const apiA = require('apiA')
const apiB = require('apiB')
const apiC = require('apiC')
describe('Main Function', () => {
  let apiAstub
  let apiBstub
  let apiCstub
  beforeEach(() => {
    apiAstub = sinon.stub(apiA, 'create')
    apiBstub = sinon.stub(apiB, 'delete')
    apiCstub = sinon.stub(apiC, 'update')
  })
  afterEach(() => {
    apiAstub.restore()
    apiBstub.restore()
    apiCstub.restore()
  })
  it('should handle errors if apiA.create() fails', () => {
    apiAstub.throws('error for apiA.create()')
    // 在这里处理一下。因为返回的是promise,catch一下就行
    return main('a', 'b', 'c').catch((e) => {
      //mocha会等promise返回或者在这里异常
      expect(e).to.equal('error for apiA.create()')
    })
  })
  it('should handle errors if apiB.delete() fails', () => {
    apiAstub.returns('success a')
    apiBstub.throws('error for apiB.delete()')
    return main('a', 'b', 'c').catch((e) => {
      expect(e).to.equal('error for apiB.create()')
    })
  })
  it('should handle errors if apiC.update() fails', () => {
    apiAstub.returns('success a')
    apiBstub.returns('success b')
    apiCstub.throws('error for apiC.delete()')
    return main('a', 'b', 'c').catch((e) => {
      expect(e).to.equal('error for apiC.create()')
    })
  })
  it('should return the responses of all functions if all api calls succeed', () => {
    apiAstub.returns('success a')
    apiBstub.returns('success b')
    apiCstub.throws('success c')
    return main('a', 'b', 'c').then((res) => {
      expect(res).to.deep.equal({
        resA: 'success a',
        resB: 'success b',
        resC: 'success c',
      })
    })
  })
})

不用第三方库,测试用例照写不误。

Async / Await和Node核心模块一起使用

你可能会想要这么写:

const fs = require('fs')
async function readThings () {
  const file = await fs.readFile('./file.txt', 'utf8')
  // file值不存在
  return file
}

但实际上:readFile 并没有返回promise,这样写是有问题的
很幸运滴是Node.js8 util方法提供了一个method,promisify:

const fs = require('fs')
const { promisify } = require('util')
const readFile = promisify(fs.readFile)
async function readThings () {
  const file = await readFile('./file.txt', 'utf8')
  // 成功!
  return file
}

Async / Await在AWS SDK中使用

const aws = require('aws-sdk')
async function getEc2Info () {
  const ec2 = new aws.EC2()
  const instances = await ec2.describeInstances()
  // do things with instances
}

如果你像上面那样写,你会发现它不起作用。你会想用util.promisify,但其实它也不能用。
实际上,这样就行了:

const aws = require('aws-sdk')
async function getEc2Info () {
  const ec2 = new aws.EC2()
  const instances = await ec2.describeInstances().promise() // <--
  // Actually do things with instances!
}

以上就是今天的内容,感谢阅读。
原文:https://start.jcolemorrison.com/5-tips-and-thoughts-on-async-await-functions/#testing

作者知乎/公众号:前端疯

原文地址:https://www.cnblogs.com/xunxing/p/bcb2eb53ef50a00496a7c5e3e21e3e07.html