在JavaScript中,有没有一种方法可以为每个函数添加try-catch?

26
为了报告错误,我想在每个函数的代码周围插入try-catch包装器。

因此,基本上我想替换

with a try-catch block.
function foo(arg){
   bar();
}

...使用...

function foo(arg){
    try {
        bar() 
    }
    catch(e){
        customErrorHandler(e)
    }
}
有没有一种方法,可以将这个通用的try-catch应用于所有函数而不需要手动编辑它们?例如,通过修改Function对象的原型? 编辑 为什么我想要在所有函数中使用try-catch:
我正在构建一个HTML5应用程序,并发布到iOS和Android上。通过我当前的基本JavaScript错误报告,我可以知道尽管该应用程序在我的设备上运行良好,但在其他某些设备上会出现错误。
我的目标有两个方面: 1.每当某个人的设备上发生JavaScript错误时,我希望通知用户该应用程序可能无法完美地运行; 2. 我想知道大致发生错误的位置,以便我知道在哪里查找问题。

1
不可以直接包装函数内的内容,除非该函数本身包含 try-catch 语句。但是,您可以将函数的 调用 放在 try-catch 块中。值得一提的是,用 try-catch 语句覆盖代码是一个相当糟糕的主意。如果出现错误,您应该解决它,而不是允许通过 try-catch 块来压制它。 - Mitya
3
感谢您想要适当地对所有函数进行 try catch 处理,但我认为您需要手动编辑它们。 - Limey
1
尝试捕获异常是非常昂贵的。您确定要这样做吗?您的需求是什么? - The Internet
1
你同样可以用一个try catch包装代码的入口点...错误会冒泡。这对于附加事件处理程序等可能不起作用,但是在第一个函数中嵌套的任何函数都将简单地将错误冒泡上来。 - KingOfHypocrites
@KingOfHypocrites,您的回答听起来很有趣,但我还没有完全理解。您所说的“用单个try-catch封装代码入口点”是什么意思?您是说我应该把所有的JavaScript代码都放在一个try{}中吗? - Wytze
6个回答

27

由于没有办法找到所有在任何地方定义的JavaScript函数,因此这并不简单。例如,任何这样的方法可能会错过在运行时定义的回调函数。

您也可能不想包装所有函数,因为那将包括浏览器函数和来自JavaScript库的函数,这些函数肯定不想要包装。

一个更好的方法可能是定义一个包装另一个函数的函数:

var tcWrapper = function(f) {
    return function() {
        try {
            f.apply(this, arguments);
        } catch(e) {
            customErrorHandler(e)
        }
    }
}

现在你可以使用这个函数来装饰任何你想要的东西。如果你使用命名空间,包装将变得更加简单:

var NS = { f: function() {  } }

只需将所有函数放到一个特殊的命名空间中,然后迭代该命名空间即可:

$.each( NS, function(i,n) {
    var p = NS[i];
    if( typeof p === 'function' ) {
        NS[i] = tcWrapper(p);
    }
} );

并非所有函数都需要相同的'this'。您可能希望意识到您正在发送哪个'this'。例如,事件处理程序有时会将此作为事件目标或对象方法。但是,您可以循环遍历对象属性并通过getter(使用Object.defineProperty)替换它们,该getter将它们包装在内联函数中,既可以将其this修复为无法从外部篡改的值,也可以将其包装在try catch块中。 - user1115652
2
@nus:我不明白你的意思。我只是传递了“this”,但也许我做错了?也许你的信息对于评论来说太复杂了。在另一个答案中展示代码和原因怎么样? - Aaron Digulla
对不起,我的错误。我没有意识到 apply() 只有在返回的函数已经附加到 NS 时才会被运行。我有些认为它是在 tcpWrapper 的上下文中调用的,在这种情况下,它将修复每个函数上来自 tcpWrapper 的 this - user1115652

12

我没有足够的声望在被接受的答案下发表评论。

我在f.apply之前添加了一个return,以便将返回值一并传递。

var tcWrapper = function(f) {
    return function() {
        try {
            return f.apply(this, arguments);
        } catch(e) {
            customErrorHandler(e)
        }
    }
}

6

给出的答案已经很好了,我只是想分享一种使用闭包的新方法。

定义包装器

const tryCatchWrapper = (executable) => async (...args) => {
    try {
        const result = await executable(...args);
        return result;
    } catch (error) {
        // use any custom handler here
       error.message = `[${executable.name}] - ${error.message}`;
       error.data = {...error.data, input_args: args}
       throw error;
    }
}

没有 try-catch 包装器的函数

const myFunction = async (x, y) => {
    const sum = x + y;
    if (sum > 10) throw new Error(`sum > 10 custom error`)
    return sum;
}

如何使用它

try {
    const wrapperFunction = trycatchWrapper3(myFunction2);
    const output = await wrapperFunction(2, 93)
    console.log(output)
} catch (error) {
    console.error(error)
}

结束


1

7
博客文章标题中说这是一种反模式,即可能但不建议使用的。由你决定,但我真的认为这不是一个好主意。 - Mitya
4
这篇博客讨论了反面模式,并展示了解决方案。 - Aaron Digulla

1

我需要加强一些代码,所以我写了一个名为fortify的函数并将其放入一个NPM模块中。它还在不断完善中,但应该会有所帮助。

https://github.com/infinitered/over-armour

奖励:它可以与异步函数一起使用。欢迎反馈。

-3

我在想(这只是一种推测,所以不确定是否可行)你是否可以像这样做:

function callTryCatch(functionSignature) {
    try {
        eval(functionSignature);
    } catch (e) {
        customErrorHandler(e);
    }
}

function entryPoint() {
    callTryCatch(function() {
        // do function logic
    });
}

这只是纯粹的猜测,我还没有进行过测试,但如果可能的话,我认为关键在于eval语句。


4
不要使用eval,在你的情况下,apply()可以实现你想要的功能。 - Aaron Digulla

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接