简单先说一下Go的defer特性:
- defer是后进先出的原则执行
- defer是在函数运行结束之后,才会运行
那么这些特性能干什么
- 延迟任务
- 尾部验证(比如运行函数之后,需要对返回值校验)
- 资源销毁
一个具体的场景
在后端开发中,可能需要多人协作,多语言,多模块开发。这时候可能就需要用RPC通信
比如有个用户模块,在进入到一个业务的时候,你需要去验证用户是否合法
async function main(username) {
const client = await rpc.createConnection(); // 创建RPC链接
const data = await client.getUserInfo(username); // 通过RPC链接获取用户信息
// 下面是业务逻辑
}
但是你发现,RPC链接并没有被关闭(即便函数执行完毕,对象client
被销毁), 它依旧占用着服务端的资源,如果多次调用这个函数,会多服务端造成很大的压力.
解决方案有两个
- 维护连接池
- 在每次创建链接之后都销毁
第一种方法是大多数ORM,RPC框架做的,应用内只保持n个链接,不断的创建再销毁,而这个过程不用使用者关心。
很可惜的是,我选用的RPC框架Thrift
,有屎一样的坑。为了保持和Go的业务逻辑一样,采用第二种方案,每次创建链接之后都销毁
,保证它是干净的
依旧是上面的代码,改成这样
async function main(username) {
const client = await rpc.createConnection(); // 创建RPC链接
try{
const data = await client.getUserInfo(username); // 通过RPC链接获取用户信息
// 下面是业务逻辑
}catch (err){
await client.close(); // 关闭链接
throw err
}
}
上面的代码已经能很好的解决问题了,但是也有一个,如果需要关闭多种链接呢(比如RPC,DB,Socket等),一直try,catch也不是办法
受到Go的defer启发,于是写了这么一个库godefer , 依旧改写上面的代码
const godefer = require('godefer');
const main = godefer(async function(username, defer) {
const client = await rpc.createConnection();
// 注册一个defer函数
defer(function() {
client.close(); // 关闭链接
});
const data = await client.getUserInfo(username);
// 下面是业务逻辑
// ...
// 运行完毕,则开始执行defer
});
Defer函数的执行,遵循了后进先出
的原则, 即 先注册的defer函数,后运行
实现上与Go差不多, 有一点重要的差别是:
Go的defer内,能修改父级函数的返回值
godefer不能修改(不是不能做,而是不做)
好了,愉快的解决问题了, 有什么想法,评论来刚啊
大牛们的评论:朕有话说
还没有人评论哦,赶紧抢沙发!