设为首页 收藏本站
查看: 807|回复: 0

[经验分享] 用NodeJs实现延迟调用,规避定时任务的闭包问题

[复制链接]
发表于 2017-2-24 10:59:15 | 显示全部楼层 |阅读模式
  很多人在用NodeJs的setTimeout(callback, delay[, arg][, ...])编写定时任务时,习惯上直接操作callback外部的对象object(闭包的特点)。这样做有一个隐患,就是当callback真正执行的时候,外部对象object可能已经被销毁了(比如执行了自定义的销毁方法),导致对object进行的处理结果出现了很大的偏差,程序甚至有可能出现异常而退出。
  解决这个问题其实很简单,我们只需要在callback回调中重新通过某种方式获取该对象,检查一下该对象是否已被销毁,即可避免上面描述的问题。但是,当程序中需要很多这样的需求时,并且是一个团队在合作写代码,这样就很难避免上述情况的发生。为了规避定时任务的闭包问题,我写了一个延迟调用类,代码如下:



/**
* script: delayCall.js
* description: 延迟调用,规避定时任务的闭包问题
* authors: alwu007@sina.cn
* date: 2016-04-19
*/
var util = require('util');
var PQueue = require('./pqueue').PQueue;
/**
* 延迟调用类
* @param search_func 对象查找函数
*/
var DelayCall = exports.DelayCall = function(search_func) {
//延迟调用序号
this._call_seq = 0;
//延迟调用映射
this._call_map = {};
//延迟调用队列
this._call_queue = new PQueue(DelayCall.compare);
//对象查找方法
this._search_func = search_func;
//设置间隔定时器。FIXME:可以改为在框架的心跳机制中去执行run方法
//注:setTimeout不支持修改系统时间
this._interval_id = setInterval(() => {
this.run();
}, 1000);
};
//比较延迟调用
DelayCall.compare = function(call1, call2) {
var time_diff = call1.exec_time - call2.exec_time;
if (time_diff) {
return time_diff;
} else {
return call1._call_id - call2._call_id;
}
};
//延迟调用序号自增
DelayCall.prototype._addSequence = function() {
return ++ this._call_seq;
};
/**
* 延迟调用
* @param id 对象查找方法_search_func根据id查找出调用者对象
* @param method_name 调用者对象上要延迟调用的方法名
* @param params 要延迟调用的方法参数
* @param delay 延迟时间,单位秒
*/
DelayCall.prototype.call = function(id, method_name, params, delay) {
var call_id = this._addSequence();
var exec_time = Date.now() + delay * 1000;
var call_elem = {
_call_id: call_id,
id: id,
method_name: method_name,
params: params,
delay: delay,
exec_time: exec_time,
_canceled: false,
};
this._call_queue.enQueue(call_elem);
this._call_map[call_id] = call_elem;
return call_id;
};
//取消延迟调用
DelayCall.prototype.cancelCall = function(call_id) {
var call_elem = this._call_map[call_id];
if (call_elem) {
delete this._call_map[call_id];
call_elem._canceled = true;
}
};
//运转一次
DelayCall.prototype.run = function() {
var now = Date.now();
var pqueue = this._call_queue;
var search_func = this._search_func;
var call_elem = pqueue.getHead();
while (call_elem) {
if (call_elem._canceled) {
pqueue.deQueue();
} else {
if (now < call_elem.exec_time) {
break;
} else {
//从队列和映射中删除
pqueue.deQueue();
delete this._call_map[call_elem._call_id];
//执行对象的方法
var obj = search_func(call_elem.id);
if (obj && typeof obj[call_elem.method_name] == 'function') {
obj[call_elem.method_name](call_elem.params);
}
}
}
call_elem = pqueue.getHead();
}
};

  PQueue的实现请参考我的饿另一篇博文:用NodeJs实现优先级队列PQueue

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-346558-1-1.html 上篇帖子: 《nodejs+gulp+webpack基础实战篇》课程笔记(三)--webpack篇 下篇帖子: 利用nodejs二次开发kibana4.4.2之环境搭建
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表