Pergunta

Estou escrevendo um "módulo" de lidar com erros globais para um dos meus aplicativos.

Um dos recursos que quero ter é poder envolver facilmente uma função com um try{} catch{} Bloco, para que todas as chamadas para essa função tenham automaticamente o código de manuseio de erros que chamam meu método global de log. (Para evitar poluir o código em todos os lugares com os blocos de tentativa/captura).

Isso está, no entanto, um pouco além da minha compreensão do funcionamento de baixo nível de JavaScript, o .call e .apply métodos e o this palavra -chave.

Eu escrevi este código, com base no protótipo de Function.wrap método:

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

Que é usado assim:

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

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

Esse código funciona perfeitamente. Ele imprime 6 e depois chama meu manipulador de erros global.

Minha pergunta é: isso quebrará algo quando a função que estou envolvendo estiver dentro de um objeto e usa o operador "esse"? Estou um pouco preocupado desde que estou ligando. Apply, passando algo lá, receio que isso possa quebrar alguma coisa.

Foi útil?

Solução

Pessoalmente, em vez de poluindo objetos incorporados, eu iria com uma técnica de decorador:

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

Você pode usá -lo assim:

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);

Mas se você tiver vontade de modificar protótipos, pode escrever assim:

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);
    }
  };
};

A melhoria óbvia será parametrizar o makerafe () para que você possa especificar qual função chamar no bloco de captura.

Outras dicas

Resposta de 2017: Basta usar ES6. Dada a seguinte função de demonstração:

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

Você pode fazer sua própria função de invólucro sem precisar 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
}

Em uso:

doThing = wrap(doThing)

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

Resposta de 2016: use o wrap módulo:

No exemplo abaixo, estou envolvendo process.exit(), mas isso funciona alegremente com qualquer outra função (incluindo o navegador JS também).

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 No Google Chrome Console me dá 'indefinido' bem, aqui está algum exemplo de funcionamento:

    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 ) );

Então, em vez de object.extend (function.prototype, {...}) faça isso como: function.prototype.extend = {}

Função de função de boa moda antiga:

//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);

O mesmo no 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);

Quanto a poluir os namespaces, na verdade vou poluir um pouco mais ... Como tudo o que acontece no JS é iniciado por algum tipo de evento, estou planejando chamar minha função mágica de invólucro de dentro do evento de protótipo .OBSERVE () Método, então não preciso chamá -lo em todos os lugares.

Eu vejo as desvantagens de tudo isso, é claro, mas esse projeto em particular está fortemente ligado ao protótipo de qualquer maneira, e quero que esse código de manipulador de erros seja o mais global possível, por isso não é grande coisa.

Obrigado pela sua resposta!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top