即使抛出错误,也可以访问函数中的所有变量?

时间:2016-04-28 16:29:43

标签: javascript error-handling

下面我有一个main功能。此函数在其中声明了许多其他变量并运行许多函数。如果函数中发生错误,我可以捕获此错误,但在我的catch中,我没有main函数的值。即使抛出错误,我也在寻找一种方法来访问所有变量。

export async function main ({alpha, beta, gamma}) {
  let one = await doSomething(alpha)
  let two = await doSomethingElse(one, beta)
  return {one, two}
}

export async function store (db) {
  await db.insert(data)
}

export async function usage (db, data) {
  try {
    let operation = await main(data)
    await store (db, operation)
    return operation
  } catch (e) {
    // here we don't have access to operation variables :(
    await store(db, {}, e.message)
    throw e
  }
}

我发现这样做的唯一合理方法是创建一个类main函数中的每个值。

import { get } from 'lodash'

export class Operation () {
  constructor(db){
    this.db = db
  }
  main({alpha, beta, gamma}) {
    this.one = await doSomething(alpha)
    this.two = await doSomethingElse(one, beta)
    return {one: this.one, two: this.two}    
  }
  init(data) {
    try {
      let main = await this.main(data)
      await this.store(main)
      return main
    } catch (e) {
      await this.store()
      throw e
    }
  }
  store(data = {}, errorMessage) {
    if (!data.one) data.one = get(this, 'one') || null
    if (!data.two) data.two = get(this, 'two') || null
    if (errorMessage) data.errMessage = errorMessage
    return await this.db.insert(data)
  }
}

即使抛出错误,如何才能访问函数中的所有变量?

2 个答案:

答案 0 :(得分:0)

简单的解决方案是使用var声明一个函数作用域的变量,而不是一个作用于let块的变量。

但是,你真的应该使用两个try / catch语句,因为你试图捕获两个不同的错误并以不同的方式处理它们:

try {
    let operation = await main(data)
    try {
        await store (db, operation)
    } catch(e) {
        // here we do have access to the `operation` variable :)
    }
    return operation
} catch (e) { // failed to create operation (or the inner catch did rethrow)
    await store(db, {}, e.message)
    throw e
}

答案 1 :(得分:0)

我创建了这个类和包装函数alwaysRunner。它接受两个函数参数,并始终为辅助函数提供可用的变量。

export class AlwaysRunner {
  constructor(operation, reguardless, optional = {}){
    Object.assign(this, optional)
    this.operation = operation.bind(this)
    this.reguardless = reguardless.bind(this)
  }
  async init(data) {
    let operation = this.operation
    let reguardless = this.reguardless
    let optional = this.optional
    delete this.operation
    delete this.reguardless
    delete this.optional
    try {
      let main = await operation(data)
      await reguardless(this)
      return main
    } catch (error) {
      await reguardless({...this, error})
      throw error
    }
  }
}

export async function alwaysRunner(operation, reguardless, optional) {
  return await new AlwaysRunner(operation, reguardless, optional).init()
}

以下是一些测试方法。

describe('alwaysRunner', () => {

  it('should work', async () => {
    let ran = [false, false]
    await alwaysRunner(function () {
      ran[0] = true
      this.alpha = true
      this.beta = true
      return this
    }, function (values) {
      ran[1] = true
      assert.equal(values.alpha, true)
      assert.equal(values.beta, true)
      assert.equal(values.error, undefined)
    }).should.not.be.rejected
    assert.deepEqual(ran, [true, true])
  })

  it('should work with thrown error', async () => {
    let ran = [false, false]
    await alwaysRunner(function () {
      ran[0] = true
      this.alpha = true
      throw new Error('some error')
      this.beta = true
    }, function (values) {
      ran[1] = true
      assert.equal(values.alpha, true)
      assert.equal(values.beta, undefined)
      assert.equal(values.error, new Error('some error'))
    }).should.be.rejected
    assert.deepEqual(ran, [true, true])
  })

})