Pregunta

Estoy escribiendo un "módulo de manejo de errores global" para una de mis aplicaciones.

Una de las características que quiero tener es poder envolver fácilmente una función con un bloque try {} catch {} , para que todas las llamadas a esa función tengan el manejo de errores automáticamente código que llamará a mi método de registro global. (Para evitar contaminar el código en todas partes con bloques try / catch).

Sin embargo, esto está un poco más allá de mi comprensión del funcionamiento de bajo nivel de JavaScript, los métodos .call y .apply , y el this palabra clave.

Escribí este código, basado en el método Function.wrap de Prototype:

Object.extend(Function.prototype, {
  TryCatchWrap: function() {
    var __method = this;
    return function() {
            try { __method.apply(this, arguments) } catch(ex) { ErrorHandler.Exception(ex); }
    }
  }
});

Que se usa así:

function DoSomething(a, b, c, d) {
    document.write(a + b + c)
    alert(1/e);
}

var fn2 = DoSomething.TryCatchWrap();
fn2(1, 2, 3, 4);

Ese código funciona perfectamente. Imprime 6 y luego llama a mi controlador de errores global.

Mi pregunta es: ¿esto romperá algo cuando la función que estoy ajustando esté dentro de un objeto y use el " this " ¿operador? Estoy un poco preocupado ya que estoy llamando .apply, pasando algo allí, me temo que esto puede romper algo.

¿Fue útil?

Solución

Personalmente, en lugar de contaminar los objetos incorporados, usaría una técnica de decorador:

var makeSafe = function(fn){
  return function(){
    try{
      return fn.apply(this, arguments);
    }catch(ex){
      ErrorHandler.Exception(ex);
    }
  };
};

Puedes usarlo así:

function fnOriginal(a){
  console.log(1/a);
};

var fn2 = makeSafe(fnOriginal);
fn2(1);
fn2(0);
fn2("abracadabra!");

var obj = {
  method1: function(x){ /* do something */ },
  method2: function(x){ /* do something */ }
};

obj.safeMethod1 = makeSafe(obj.method1);
obj.method1(42);     // the original method
obj.safeMethod1(42); // the "safe" method

// let's override a method completely
obj.method2 = makeSafe(obj.method2);

Pero si tiene ganas de modificar prototipos, puede escribirlo así:

Function.prototype.TryCatchWrap = function(){
  var fn = this; // because we call it on the function itself
  // let's copy the rest from makeSafe()
  return function(){
    try{
      return fn.apply(this, arguments);
    }catch(ex){
      ErrorHandler.Exception(ex);
    }
  };
};

La mejora obvia será parametrizar makeSafe () para que pueda especificar qué función llamar en el bloque catch.

Otros consejos

respuesta 2017 : solo use ES6. Dada la siguiente función de demostración:

var doThing = function(){
  console.log(...arguments)
}

Puede crear su propia función de contenedor sin necesidad de bibliotecas externas:

var wrap = function(someFunction){
  var wrappedFunction = function(){
    var args = [...arguments].splice(0)
    console.log(`You're about to run a function with these arguments: \n     ${args}`)
    return someFunction(args)
  }
  return wrappedFunction
}

En uso:

doThing = wrap(doThing)

doThing('one', {two:'two'}, 3)

Respuesta 2016 : use el módulo wrap :

En el siguiente ejemplo, estoy envolviendo process.exit () , pero esto funciona felizmente con cualquier otra función (incluido el navegador JS también).

var wrap = require('lodash.wrap');

var log = console.log.bind(console)

var RESTART_FLUSH_DELAY = 3 * 1000

process.exit = wrap(process.exit, function(originalFunction) {
    log('Waiting', RESTART_FLUSH_DELAY, 'for buffers to flush before restarting')
    setTimeout(originalFunction, RESTART_FLUSH_DELAY)
});

process.exit(1);

Object.extend (Function.prototype, { Object.extend en la consola Google Chrome me da 'indefinido' Bueno, aquí hay un ejemplo de trabajo:

    Boolean.prototype.XOR =
//  ^- Note that it's a captial 'B' and so
//      you'll work on the Class and not the >b<oolean object
        function( bool2 ) { 

           var bool1 = this.valueOf();
           //         'this' refers to the actual object - and not to 'XOR'

           return (bool1 == true   &&   bool2 == false)
               || (bool1 == false   &&   bool2 == true);
        } 

alert ( "true.XOR( false ) => " true.XOR( false ) );

así que en lugar de Object.extend (Function.prototype, {...}) Hazlo como: Function.prototype.extend = {}

Envoltura de funciones a la antigua usanza:

//Our function
function myFunction() {
  //For example we do this:
  document.getElementById('demo').innerHTML = Date();
  return;
}

//Our wrapper - middleware
function wrapper(fn) {
  try {
    return function(){
      console.info('We add something else', Date());
      return fn();
    }
  }
  catch (error) {
    console.info('The error: ', error);
  }
}

//We use wrapper - middleware
myFunction = wrapper(myFunction);

Lo mismo en el estilo ES6:

//Our function
let myFunction = () => {
  //For example we do this:
  document.getElementById('demo').innerHTML = Date();
  return;
}

//Our wrapper - middleware
const wrapper = func => {
  try {
    return () => {
      console.info('We add something else', Date());
      return func();
    }
  }
  catch (error) {
    console.info('The error: ', error);
  }
}

//We use wrapper - middleware
myFunction = wrapper(myFunction);

En cuanto a contaminar los espacios de nombres, en realidad voy a contaminarlos un poco más ... Como todo lo que sucede en JS se inicia por un evento de algún tipo, estoy planeando llamar a mi función mágica de envoltura desde el método Prototype Event.observe (), por lo que no necesito llamarlo a todas partes.

Sí veo las desventajas de todo esto, por supuesto, pero este proyecto en particular está muy relacionado con Prototype de todos modos, y quiero que este código de controlador de errores sea lo más global posible, por lo que no es gran cosa.

¡Gracias por tu respuesta!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top