try node manejo errores catch javascript exception-handling

node - Excepciones personalizadas en JavaScript



manejo de errores node js (13)

¿Puedo definir tipos personalizados para excepciones definidas por el usuario en JavaScript? Si puedo, ¿cómo lo haría?


A menudo utilizo un enfoque con herencia prototípica. La anulación de toString() le brinda la ventaja de que herramientas como Firebug registrarán la información real en lugar de [object Object] en la consola para las excepciones no detectadas.

Utilice instanceof para determinar el tipo de excepción.

main.js

// just an exemplary namespace var ns = ns || {}; // include JavaScript of the following // source files here (e.g. by concatenation) var someId = 42; throw new ns.DuplicateIdException(''Another item with ID '' + someId + '' has been created''); // Firebug console: // uncaught exception: [Duplicate ID] Another item with ID 42 has been created

Exception.js

ns.Exception = function() { } /** * Form a string of relevant information. * * When providing this method, tools like Firebug show the returned * string instead of [object Object] for uncaught exceptions. * * @return {String} information about the exception */ ns.Exception.prototype.toString = function() { var name = this.name || ''unknown''; var message = this.message || ''no description''; return ''['' + name + ''] '' + message; };

DuplicateIdException.js

ns.DuplicateIdException = function(message) { this.name = ''Duplicate ID''; this.message = message; }; ns.DuplicateIdException.prototype = new ns.Exception();


Aquí es cómo puede crear errores personalizados con un comportamiento completamente idéntico al Error nativo. Esta técnica solo funciona en Chrome y node.js por ahora. Tampoco recomendaría usarlo si no entiendes lo que hace.

Error.createCustromConstructor = (function() { function define(obj, prop, value) { Object.defineProperty(obj, prop, { value: value, configurable: true, enumerable: false, writable: true }); } return function(name, init, proto) { var CustomError; proto = proto || {}; function build(message) { var self = this instanceof CustomError ? this : Object.create(CustomError.prototype); Error.apply(self, arguments); Error.captureStackTrace(self, CustomError); if (message != undefined) { define(self, ''message'', String(message)); } define(self, ''arguments'', undefined); define(self, ''type'', undefined); if (typeof init == ''function'') { init.apply(self, arguments); } return self; } eval(''CustomError = function '' + name + ''() {'' + ''return build.apply(this, arguments); }''); CustomError.prototype = Object.create(Error.prototype); define(CustomError.prototype, ''constructor'', CustomError); for (var key in proto) { define(CustomError.prototype, key, proto[key]); } Object.defineProperty(CustomError.prototype, ''name'', { value: name }); return CustomError; } })();

Como resultado obtenemos

/** * name The name of the constructor name * init User-defined initialization function * proto It''s enumerable members will be added to * prototype of created constructor **/ Error.createCustromConstructor = function(name, init, proto)

Entonces puedes usarlo así:

var NotImplementedError = Error.createCustromConstructor(''NotImplementedError'');

Y usa NotImplementedError como lo harías con un Error :

throw new NotImplementedError(); var err = new NotImplementedError(); var err = NotImplementedError(''Not yet...'');

Y se comportará como se espera:

err instanceof NotImplementedError // -> true err instanceof Error // -> true NotImplementedError.prototype.isPrototypeOf(err) // -> true Error.prototype.isPrototypeOf(err) // -> true err.constructor.name // -> NotImplementedError err.name // -> NotImplementedError err.message // -> Not yet... err.toString() // -> NotImplementedError: Not yet... err.stack // -> works fine!

Tenga en cuenta que error.stack funciona absolutamente correctamente y no incluirá la NotImplementedError constructor NotImplementedError (gracias a Error.captureStackTrace() v8).

Nota. Hay eval() fea eval() . La única razón por la que se usa es para obtener el err.constructor.name correcto.constructor.name. Si no lo necesitas, puedes simplificarlo un poco todo.


Debería crear una excepción personalizada que prototípicamente hereda de Error. Por ejemplo:

function InvalidArgumentException(message) { this.message = message; // Use V8''s native method if available, otherwise fallback if ("captureStackTrace" in Error) Error.captureStackTrace(this, InvalidArgumentException); else this.stack = (new Error()).stack; } InvalidArgumentException.prototype = Object.create(Error.prototype); InvalidArgumentException.prototype.name = "InvalidArgumentException"; InvalidArgumentException.prototype.constructor = InvalidArgumentException;

Básicamente, se trata de una versión simplificada de lo que se publicó anteriormente con la mejora de que los seguimientos de pila funcionan en Firefox y otros navegadores. Satisface las mismas pruebas que publicó:

Uso:

throw new InvalidArgumentException(); var err = new InvalidArgumentException("Not yet...");

Y se comportará como se espera:

err instanceof InvalidArgumentException // -> true err instanceof Error // -> true InvalidArgumentException.prototype.isPrototypeOf(err) // -> true Error.prototype.isPrototypeOf(err) // -> true err.constructor.name // -> InvalidArgumentException err.name // -> InvalidArgumentException err.message // -> Not yet... err.toString() // -> InvalidArgumentException: Not yet... err.stack // -> works fine!


Desde WebReference :

throw { name: "System Error", level: "Show Stopper", message: "Error detected. Please contact the system administrator.", htmlMessage: "Error detected. Please contact the <a href=/"mailto:[email protected]/">system administrator</a>.", toString: function(){return this.name + ": " + this.message;} };


Podría implementar sus propias excepciones y su manejo, por ejemplo, como aquí:

// define exceptions "classes" function NotNumberException() {} function NotPositiveNumberException() {} // try some code try { // some function/code that can throw if (isNaN(value)) throw new NotNumberException(); else if (value < 0) throw new NotPositiveNumberException(); } catch (e) { if (e instanceof NotNumberException) { alert("not a number"); } else if (e instanceof NotPositiveNumberException) { alert("not a positive number"); } }

Hay otra sintaxis para capturar una excepción escrita, aunque esto no funcionará en todos los navegadores (por ejemplo, no en IE):

// define exceptions "classes" function NotNumberException() {} function NotPositiveNumberException() {} // try some code try { // some function/code that can throw if (isNaN(value)) throw new NotNumberException(); else if (value < 0) throw new NotPositiveNumberException(); } catch (e if e instanceof NotNumberException) { alert("not a number"); } catch (e if e instanceof NotPositiveNumberException) { alert("not a positive number"); }


Sí. Puedes lanzar lo que quieras: enteros, cadenas, objetos, lo que sea. Si desea lanzar un objeto, simplemente cree un nuevo objeto, tal como lo haría en otras circunstancias, y luego lance. La referencia de JavaScript de Mozilla tiene varios ejemplos.


Una alternativa a la respuesta de asselin para usar con las clases de ES2015

class InvalidArgumentException extends Error { constructor(message) { super(); Error.captureStackTrace(this, this.constructor); this.name = "InvalidArgumentException"; this.message = message; } }


Usa la declaración de throw .

A JavaScript no le importa cuál es el tipo de excepción (como a Java). JavaScript solo se da cuenta, hay una excepción y cuando la detectas, puedes "ver" lo que dice la excepción ".

Si tiene diferentes tipos de excepción que tiene que lanzar, sugeriría usar variables que contengan la cadena / objeto de la excepción, es decir, el mensaje. Donde lo necesite, use "throw myException" y en la captura, compare la excepción capturada con myException.


Vea este ejemplo en el MDN.

Si necesita definir varios errores (pruebe el código here ):

function createErrorType(name, initFunction) { function E(message) { this.message = message; if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor); else this.stack = (new Error()).stack; initFunction && initFunction.apply(this, arguments); } E.prototype = Object.create(Error.prototype); E.prototype.name = name; E.prototype.constructor = E; return E; } var InvalidStateError = createErrorType( ''InvalidStateError'', function (invalidState, acceptedStates) { this.message = ''The state '' + invalidState + '' is invalid. Expected '' + acceptedStates + ''.''; }); var error = new InvalidStateError(''foo'', ''bar or baz''); function assert(condition) { if (!condition) throw new Error(); } assert(error.message); assert(error instanceof InvalidStateError); assert(error instanceof Error); assert(error.name == ''InvalidStateError''); assert(error.stack); error.message;

El código se copia principalmente de: ¿Cuál es una buena manera de extender el error en JavaScript?


ES6

Con las nuevas palabras clave de clase y extensión ahora es mucho más fácil:

class CustomError extends Error { constructor(message) { super(message); //something } }


En breve:

Opción 1: usar babel-plugin-transform-builtin-extend

Opción 2: hazlo tú mismo (inspirado en esa misma biblioteca)

function CustomError(...args) { const instance = Reflect.construct(Error, args); Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this)); return instance; } CustomError.prototype = Object.create(Error.prototype, { constructor: { value: Error, enumerable: false, writable: true, configurable: true } }); Reflect.setPrototypeOf(CustomError, Error);

  • Si está utilizando ES5 puro :

    function CustomError(message, fileName, lineNumber) { const instance = new Error(message, fileName, lineNumber); Object.setPrototypeOf(instance, Object.getPrototypeOf(this)); return instance; } CustomError.prototype = Object.create(Error.prototype, { constructor: { value: Error, enumerable: false, writable: true, configurable: true } }); if (Object.setPrototypeOf){ Object.setPrototypeOf(CustomError, Error); } else { CustomError.__proto__ = Error; }

  • Alternativa: utilizar el marco Classtrophobic .

Explicación:

¿Por qué extender la clase Error usando ES6 y Babel es un problema?

Porque una instancia de CustomError ya no se reconoce como tal.

class CustomError extends Error {} console.log(new CustomError(''test'') instanceof Error);// true console.log(new CustomError(''test'') instanceof CustomError);// false

De hecho, a partir de la documentación oficial de Babel, no puede extender ninguna clase incorporada de JavaScript como Date , Array , DOM o Error .

El problema se describe aquí:

¿Qué pasa con las otras respuestas de SO?

Todas las respuestas dadas solucionan el problema de instanceof pero pierde el error regular console.log :

console.log(new CustomError(''test'')); // output: // CustomError {name: "MyError", message: "test", stack: "Error↵ at CustomError (<anonymous>:4:19)↵ at <anonymous>:1:5"}

Mientras usa el método mencionado anteriormente, no solo soluciona el problema de instanceof , sino que también mantiene el error regular console.log :

console.log(new CustomError(''test'')); // output: // Error: test // at CustomError (<anonymous>:2:32) // at <anonymous>:1:5


//create error object var error = new Object(); error.reason="some reason!"; //business function function exception(){ try{ throw error; }catch(err){ err.reason; } }

Ahora establecemos agregar el motivo o las propiedades que queramos al objeto de error y recuperarlo. Haciendo el error más razonable.


function MyError(message) { this.message = message; } MyError.prototype = new Error;

Esto permite su uso como ...

try { something(); } catch(e) { if(e instanceof MyError) doSomethingElse(); else if(e instanceof Error) andNowForSomethingCompletelyDifferent(); }