新闻资讯

新闻资讯 行业动态

前端函数鲁棒性的几种方式

编辑:008     时间:2020-02-17

1、入参要鲁棒性

在 ES6+ 到来后,函数的入参写法已经得到了质的提高和优化。看下面代码:

function print(obj = {}) { console.log('name', obj.name) console.log('age', obj.age)
}

print 函数,入参是 obj 通过 obj = {} 来给入参设置默认的参数值,从而提高入参的鲁棒性。

同时会发现,如果入参的默认值是 {} ,那函数里面的 obj.name 就会是 undefined ,这也不够鲁棒,所以下面就要说说函数内表达式语句的鲁棒性了。

2、函数内表达式语句要鲁棒性

继续上个例子:

function print(obj = {}{ console.log('name:', obj.name || '未知姓名') console.log('age:', obj.age || '未知年龄')
}

如果这样的话,表达式语句变得比较鲁棒性了,但还不够抽象,我们换种方式稍微把表达式语句给解耦一下,代码如下:

function print(obj = {}{ const { name = '未知姓名', age = '未知年龄' } = obj console.log('name:', name console.log('age:', age)
}

上述代码其实还可以再抽象,比如把 console.log 封装成 log 函数,通过调用 log(name) ,就能完成 console.log('name:', name) 的功能。

3、函数异常处理的两个层面

  • 防患于未然,从一开始就不要让异常发生。

  • 异常如果出现了,该怎么去处理出现的异常。

那如何去更好的处理各种异常,提高函数的鲁棒性呢,我个人有以下几点看法。

4、推导一下 try/catch 的原理

js 在 node.js 提供的运行时环境中运行,node.js 是用 C++ 写的。C++ 有自己的异常处理机制,也是有 try/catch 。即 js 的 try/catch 的底层实现是直接通过桥,调用 C++ 的 try/catch 。

而 C++ 的 try/catch 具有一些特性,如try/catch 只能捕捉当前线程的异常。这样就解释了为什么 JS 的 try/catch 只能捕捉到同步的异常,而对于异步的异常就无能为力了(因为异步是放在另一个线程中执行的)。

这里是我的推导,不代表确切答案。

5、合理的处理异常

第一个方法:如果是同步操作,可以用 throw 来传递异常

看下面代码:

try { throw new Error('hello godkun, i am an Error ') console.log('throw 之后的处代码不执行')
} catch (e) { console.log(e.message)
}

首先 throw 是以同步的方式传递异常的,也就是 throw 要和使用 throw 传递错误的函数拥有相同的上下文环境。

如果上下文环境中,都没有使用 try/catch 的话,但是又 throw 了异常,那么程序大概率会崩溃。

如果是 nodejs ,此时应该再加一个进程级的 uncaughtException 来捕捉这种没有被捕捉的异常。通常还会加上 unhandledRejection 的异常处理。

第二个方法:如果是异步的操作

有三种方式:

  1. 使用 callback ,比如 nodejs 的 error first 风格。

  2. 对于复杂的情况可以使用基于 Event 的方式来做,调用者来监听对象的 error 事件。

  3. 使用 promise 和 async/await 来捕捉异常。

怎么去选择哪个方式呢?依据以下原则:

  1. 简单的场景,直接使用 promise 和 async/await来捕捉异常。

  2. 复杂的场景,比如可能会产生多个错误,这个时候最好用 Event 的方式。

第三个方法:如果既有异步操作又有同步操作

最好的方式就是使用最新的语法:async/await 来结合 promise 和 try/catch 来完成对既有同步操作又有异步操作的异常捕捉。

第四个方法:处理异常的一些抽象和封装

对处理异常的函数进行抽象和封装也是提高函数质量的一个途径。如何对处理异常进行抽象和封装呢?有几个方式可以搞定它:

  1. 第一种方式:对 nodejs 来说,通常将异常处理封装成中间件,比如基于 express/koa 的异常中间件,通常情况下,处理异常的中间件要作为最后一个中间件加载,目的是为了捕获之前的所有中间件可能出现的错误。

  2. 第二种方式:对前端或者 nodejs 来说,可以将异常处理封装成模块,类似 Event 的那种。

  3. 第三种方式:使用装饰器模式,对函数装饰异常处理模块,比如通过装饰器对当前函数包裹一层 try/catch 。

  4. 第四种方式:使用函数式编程中的函子( Monad )等来对异常处理进行统一包裹,这里 的 Monad 和 try/catch 在表现上都相当于一个容器,这是一个相当强大的方法。从 Monad 可以扩展出很多异常处理的黑科技,但是我建议慎用,因为不是所有人都能看懂的,要考虑团队的整体技术能力,当然一个人的话,那就随便嗨了。

合理的处理异常,根据具体情况来确定使用合理的方式处理异常

三、如何确保单个节点出问题,不会影响整个登录流程

比如登录流程需要4个安全验证,按照通常的写法,其中一个挂了,那就全部挂了,但是这不够鲁棒性,如何去解决这个问题呢。

主要方案就使用将 promise 的链式写法换一种方式写,以前的写法是这样的:

伪代码如下:

auth().then(getIP).then(getToken).then(autoLogin).then(xxx).catch(function(){})

经过鲁棒调整后,可以改成如下写法:

伪代码如下:

auth().catch(goAuthErrorHandle).then(getIP).catch(goIPErrorHandle).then(function(r){})

经过微调后的代码,直接让登录流程的鲁棒性提升了很多,就算出错也可以通过错误处理后,继续传递到下一个方法中。

四、我个人对异常处理的看法

我个人认为对异常的处理,还是要根据实际情况来分析的。大概有以下几点看法:

要考虑项目可维护性,团队技术水平

我曾在一个需求中,使用了诸如函子等较为抽象的处理异常的方法,虽然秀了一把(作死),结果导致后续这块的需求改动,还得我自己来。

要提前预估好项目的复杂性和重要性。

比如在做一个比较重要的业务时,一开始没有想到异常处理需要这么细节,而且一般第一版的时候,需求并没有涉及到很多异常情况处理,但是后续需求迭代优化的时候,发现异常情况处理是如此的多,直接导致需要重写异常处理相关的代码。

所以以后在项目评估的时候,要学会尝试根据项目的重要性,来提前预留好坑位。

这也算是一种面对未来的编程模式。

五、总结

关于函数的鲁棒性(防御性编程),本文主要介绍了前端或者是 nodejs 处理异常的常规方法。

处理异常不是一个简单的活,工作中还得结合业务去确定合适的异常处理方式,总之,多多实践出真知。

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。

回复列表

相关推荐