处理JavaScript中的特定错误(想想异常)

时间:2009-09-16 15:00:25

标签: javascript error-handling

你将如何实现不同类型的错误,这样你就能捕捉到特定错误并让其他人冒出来......?

实现此目的的一种方法是修改Error对象的原型:

Error.prototype.sender = "";


function throwSpecificError()
{
    var e = new Error();

    e.sender = "specific";

    throw e;
}

捕获特定错误:

try
{
    throwSpecificError();
}

catch (e)
{
    if (e.sender !== "specific") throw e;

    // handle specific error
}


你们有其他选择吗?

7 个答案:

答案 0 :(得分:120)

要创建自定义例外,您可以从Error对象继承:

function SpecificError () {

}

SpecificError.prototype = new Error();

// ...
try {
  throw new SpecificError;
} catch (e) {
  if (e instanceof SpecificError) {
   // specific error
  } else {
    throw e; // let others bubble up
  }
}

没有继承自Error的简约方法可能是抛出一个具有名称和消息属性的简单对象:

function throwSpecificError() {
  throw {
    name: 'SpecificError',
    message: 'SpecificError occurred!'
  };
}


// ...
try {
  throwSpecificError();
} catch (e) {
  if (e.name == 'SpecificError') {
   // specific error
  } else {
    throw e; // let others bubble up
  }
}

答案 1 :(得分:14)

如下面的评论中所述,这是特定于Mozilla的,但您可以使用'条件捕获'块。 e.g:

try {
  ...
  throwSpecificError();
  ...
}
catch (e if e.sender === "specific") {
  specificHandler(e);
}
catch (e if e.sender === "unspecific") {
  unspecificHandler(e);
}
catch (e) {
  // don't know what to do
  throw e;
} 

这更类似于Java中使用的类型化异常处理,至少在语法上如此。

答案 2 :(得分:8)

try-catch-finally.js

实施例

_try(function () {
    throw 'My error';
})
.catch(Error, function (e) {
    console.log('Caught Error: ' + e);
})
.catch(String, function (e) {
    console.log('Caught String: ' + e);
})
.catch(function (e) {
    console.log('Caught other: ' + e);
})
.finally(function () {
    console.log('Error was caught explicitly');
});

答案 3 :(得分:1)

出口使用模块

load_data()

导入脚本:

/**
 * Custom InputError
 */
class InputError extends Error {
  /**
   * Create InputError
   * @param {String} message
   */
  constructor(message) {
    super(message);
    this.name = this.constructor.name;
    Error.captureStackTrace(this, this.constructor);
  }
}

/**
 * Custom AuthError
 */
class AuthError extends Error {
  /**
   * Create AuthError
   * @param {String} message
   */
  constructor(message) {
    super(message);
    this.name = this.constructor.name;
    Error.captureStackTrace(this, this.constructor);
  }
}

/**
 * Custom NotFoundError
 */
class NotFoundError extends Error {
  /**
   * Create NotFoundError
   * @param {String} message
   */
  constructor(message) {
    super(message);
    this.name = this.constructor.name;
    Error.captureStackTrace(this, this.constructor);
  }
}

module.exports = {
  InputError: InputError,
  AuthError: AuthError,
  NotFoundError: NotFoundError
};

使用:

const {InputError, AuthError, NotFoundError} = require(path.join(process.cwd(), 'lib', 'errors'));

外部调用代码:

function doTheCheck = () =>
  checkInputData().then(() => {
    return Promise.resolve();
  }).catch(err => {
    return Promise.reject(new InputError(err));
  });
};

答案 4 :(得分:0)

我不喜欢上述任何一种解决方案,所以我自己做了。 try-catch-finally.js非常酷,只是如果您在尝试之前忘记了一个下划线(_),那么代码仍然可以正常运行,但是任何东西都不会被捕获!好吧。

CatchFilter

我在代码中添加了一个CatchFilter:

"use strict";

/**
 * This catches a specific error. If the error doesn't match the errorType class passed in, it is rethrown for a
 * different catch handler to handle.
 * @param errorType The class that should be caught
 * @param funcToCall The function to call if an error is thrown of this type
 * @return {Function} A function that can be given directly to the `.catch()` part of a promise.
 */
module.exports.catchOnly = function(errorType, funcToCall) {
  return (error) => {
    if(error instanceof errorType) {
      return funcToCall(error);
    } else {
      // Oops, it's not for us.
      throw error;
    }
  };
};

现在我可以过滤

现在我可以像在C#或Java中那样进行过滤了:

new Promise((resolve, reject => {
   <snip><snip>
}).catch(CatchFilter.catchOnly(MyError, err =>
   console.log("This is for my error");
}).catch(err => {
   console.log("This is for all of the other errors.");
});

答案 5 :(得分:0)

不幸的是,没有“官方”的方式可以在 Javascript 中实现这个基本功能。我将分享我在不同包中看到的三种最常见的解决方案,以及如何在现代 Javascript (es6+) 中实现它们,以及它们的一些优缺点。

1.将 Error 类子类化

在 es6 中对“Error”的实例进行子类化变得更加容易。只需执行以下操作:

class FileNotFoundException extends Error {
  constructor(message) {
    super(message)
    // Not required, but makes uncaught error messages nicer.
    this.name = 'FileNotFoundException'
  }
}

完整示例:

class FileNotFoundException extends Error {
  constructor(message) {
    super(message)
    // Not required, but makes uncaught error messages nicer.
    this.name = 'FileNotFoundException'
  }
}

// Example usage

function readFile(path) {
  throw new FileNotFoundException(`The file ${path} was not found`)
}

try {
  readFile('./example.txt')
} catch (err) {
  if (err instanceof FileNotFoundException) {
    // Handle the custom exception
    console.log(`Could not find the file. Reason: ${err.message}`)
  } else {
    // Rethrow it - we don't know how to handle it
    // The stacktrace won't be changed, because
    // that information is attached to the error
    // object when it's first constructed.
    throw err
  }
}

如果您不喜欢将 this.name 设置为硬编码字符串,您可以将其设置为 this.constructor.name,这将给出您的类的名称。这样做的好处是您的自定义异常的任何子类都不需要更新 this.name,因为 this.constructor.name 将是子类的名称。

与某些替代解决方案相比,子类异常的优势在于它们可以提供更好的编辑器支持(例如自动完成)。您可以轻松地向特定异常类型添加自定义行为,例如附加函数、替代构造函数参数等。在提供自定义行为或数据时,支持 typescript 也往往更容易。

有很多关于如何正确子类化 Error 的讨论。例如,如果您使用的是转译器,上述解决方案可能不起作用。有些人建议使用特定于平台的 captureStackTrace() 如果它可用(虽然我在使用它时没有注意到错误有任何不同 - 也许它不再相关了?‍♂️)。要了解更多信息,请参阅 this MDN 页面和 This Stackoverflow 答案。

许多浏览器 API 都走这条路并抛出自定义异常(如 here 所示)

2.为错误添加区分属性

这个想法很简单。创建您的错误,在您的错误中添加一个额外的属性,例如“代码”,然后抛出它。

const error = new Error(`The file ${path} was not found`)
error.code = 'NotFound'
throw error

完整示例:

function readFile(path) {
  const error = new Error(`The file ${path} was not found`)
  error.code = 'NotFound'
  throw error
}

try {
  readFile('./example.txt')
} catch (err) {
  if (err.code === 'NotFound') {
    console.log(`Could not find the file. Reason: ${err.message}`)
  } else {
    throw err
  }
}

当然,您可以创建一个辅助函数来删除一些样板并确保一致性。

此解决方案的优点是您无需导出包可能引发的所有可能异常的列表。您可以想象,例如,如果您的包一直使用 NotFound 异常来指示特定函数无法找到预期资源,那会变得多么尴尬。您想添加一个 addUserToGroup() 函数,该函数理想情况下会根据未找到的资源抛出 UserNotFound 或 GroupNotFound 异常。对于子类异常,您将不得不做出棘手的决定。使用错误对象上的代码,您就可以做到。

这是路由节点的 fs 模块采取的异常。如果您尝试读取一个不存在的文件,它会抛出一个带有一些附加属性的错误实例,例如 code,它会针对该特定异常将其设置为 "ENOENT"

3.返回您的异常。

谁说你必须扔掉它们?在某些情况下,只返回出错的地方可能最有意义。

function readFile(path) {
  if (itFailed()) {
    return { exCode: 'NotFound' }
  } else {
    return { data: 'Contents of file' }
  }
}

在处理大量异常时,像这样的解决方案可能最有意义。这样做很简单,并且可以帮助自我记录哪些函数给出了哪些异常,这使得代码更加健壮。缺点是它会给你的代码增加很多膨胀。

完整示例:

function readFile(path) {
  if (Math.random() > 0.5) {
    return { exCode: 'NotFound' }
  } else {
    return { data: 'Contents of file' }
  }
}

function main() {
  const { data, exCode } = readFile('./example.txt')

  if (exCode === 'NotFound') {
    console.log('Could not find the file.')
    return
  } else if (exCode) {
    // We don't know how to handle this exCode, so throw an error
    throw new Error(`Unhandled exception when reading file: ${exCode}`)
  }

  console.log(`Contents of file: ${data}`)
}
main()

非解决方案

其中一些解决方案感觉工作量很大。只是抛出一个对象文字很诱人,例如throw { code: 'NotFound' }不要这样做!堆栈跟踪信息附加到错误对象。如果这些对象文字中的一个曾经漏掉并成为未捕获的异常,您将无法通过堆栈跟踪来了解它发生的位置或方式。一般来说,调试会困难得多。

奖励:显式异常

当您的包开始处理大量异常时,我建议使用如上所述的“返回异常”方法,以帮助您跟踪哪些异常可能来自何处(至少在您的包内部使用它 -你仍然可以为你的包用户扔东西)。有时,即使是这种解决方案也不够好。我整理了一些显式异常包来帮助处理这些情况(可以在 here 中找到。还有一个轻量版本,您可以将其复制粘贴到您的项目 here 中)。这个想法是要求用户明确列出他们希望函数调用提供哪些异常。这使得跟踪异常路径变得非常容易。

try {
  // readFile() will give an exception.
  // In unwrap(), you list which exceptions you except, to help with self-documentation.
  // runtime checks help ensure no other kinds of exceptions slip through.
  return unwrap(readFile('./example.txt'), ['NotFound'])
} catch (err) {
  // Handle the exception here
}

完整示例:

// You must have the explicit-exceptions package for this code to run
const { Exception, wrap, unwrap } = require('explicit-exceptions')

const readFile = wrap(path => {
  throw new Exception('NotFound')
})

function main () {
  try {
    return unwrap(readFile('./example.txt'), ['NotFound'])
  } catch (ex) {
    if (!(ex instanceof Exception)) throw ex // Not an exception, don't handle it
    console.assert(ex.code === 'NotFound')
    console.log('File not found')
  }
}

main()

答案 6 :(得分:-3)

s