很多人在用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

用NodeJs实现延迟调用,规避定时任务的闭包问题的更多相关文章

  1. 【Unity3D】Invoke,InvokeRepeating ,Coroutine 延迟调用,周期性调用

    Invoke和InvokeRepeating方法,可以实现延迟调用,和周期调用 第一个是执行一次,第二个是重复执行 void Invoke(string methodName, float time) ...

  2. xcode UIImageView创建、图片加载、 音频文件播放、 延迟调用

    代码创建 /** 创建UIImageView */ UIImageView * imageView=[[UIImageView alloc]init]; /** 设置尺寸位置 */ imageView ...

  3. 延迟调用或多次调用第三方的Web API服务

    当我们调用第三方的Web API服务的时候,不一定每次都是成功的.这时候,我们可能会再多尝试几次,也有可能延迟一段时间再去尝试调用服务. Task的静态方法Delay允许我们延迟执行某个Task,此方 ...

  4. ios 延迟调用 && UIImageView && UILabel && UISegmentedControl && UISwitch && UISlider

    // //  ViewController.m //  UI_Lesson3 // //  Created by archerzz on 15/8/13. //  Copyright (c) 2015 ...

  5. 17_defer(延迟调用)关键字的使用

    1.defer是延迟调用关键字,只能在函数内部使用 2.总是在main函数结束前调用(和init用法相对) 3.如果有多个defer 遵循先进后出的原则 4.和匿名函数同时使用时,如果匿名函数带有参数 ...

  6. Go语言系列开发之延迟调用和作用域

    Hello,各位小伙伴大家好,我是小栈君,最近一段时间我们将继续分享关于go语言基础系列,当然后期小栈君已经在筹划关于java.Python,数据分析.人工智能和大数据等相关系列文章.希望能和大家一起 ...

  7. defer 延迟调用

    1. 延迟调用 defer 的用法很简单,只要在后面跟一个函数的调用,就能实现将这个  xxx 函数的调用延迟到当前函数执行完后再执行. defer xxx()   这是一个很简单的例子,可以很快帮助 ...

  8. 在Nodejs中如何调用C#的代码

    最近需要在Nodejs中用到C#的代码,从网上了解到可以采用Edgejs来实现Nodejs与C#的代码交互, 直接复制网上的代码运行总是出各种错,填了不少坑,现在把自己的案例代码大致整理一下,方便以后 ...

  9. Twisted 延迟调用

    延迟(defer)是twisted框架中实现异步的编程体系,使程序设计可以采用事件驱动的机制 1.基本使用 defer可以看作一个管理回调函数的对象,可以向该对象添加需要的回调函数同时也可以指定该组函 ...

随机推荐

  1. Http协议Get方式获取图片

    一.                二.                         我试了试,Post方式也行啊,干嘛要叫强调Get方式,费解~~       答曰:get是向服务器请求数据,p ...

  2. Http 与 Socket 区别

    HTTP:超文本传输协议,首先它是一个协议,并且是基于TCP/IP协议基础之上的应用层协议.TCP/IP协议是传输层协议,主要解决数据如何在网络中传输,HTTP是应用层协议,主要解决如何包装数据.HT ...

  3. POJ 1778 All Discs Considered(拓扑排序)

    点我看题目 题意 :其实题意我也说不清楚,因为比赛的时候我盯着看了1个小时也没看懂....就是两个磁盘,第一个有n1的安装包,编号为1~n1,第二个有n2个安装包,编号为n1~n2.给你d对关系,(x ...

  4. Myeclipse2013 SVN安装方法以及项目上传到svn服务器

    1. 打开 Myeclipse 工具栏下的Help下的Install from Site 2.打开后弹出窗口, 并点击Add标签,如下图: 3.现在是最重要的一步,填写相关信息. 在对话框Name输入 ...

  5. Android代码中使用Ping命令

    项目中需要搜索同一WIFI局域网中的设备并进行通信,暂时想到的办法是得到局域网网段的地址,因为同一局域网中的IP地址前三位是相同的,而第四位的范围从0~250,所以对第四位进行遍历搜索,能ping通的 ...

  6. iframe的安全问题

    今天尝试在iframe中嵌入外部网站, 碰到了一些小问题. 如何让自己的网站不被其他网站的iframe引用? 我测试的时候发现我把iframe的src指定到github不起作用. 原来是它把X-Fra ...

  7. Linux中的随机数文件 /dev/random /dev/urandom

    Linux中的随机数可以从两个特殊的文件中产生,一个是/dev/urandom.另外一个是/dev/random.他们产生随机数的原理是利用当前系统的熵池来计算出固定一定数量的随机比特,然后将这些比特 ...

  8. com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: SELECT command denied to user’

    Linux环境 Mysql+Hibernate command denied to user 错误 错误信息 如下: com.mysql.jdbc.exceptions.jdbc4.MySQLSynt ...

  9. statspack系列7

    原文:http://jonathanlewis.wordpress.com/2006/12/27/analysing-statspack-7/ 作者:Jonathan Lewis 这是一段Oracle ...

  10. Android 在webView中创建web应用(译文)

    如果你想在客户端添加一个web应用程序或者仅仅一个web页面,你可以通过使用WebView,WebView是基于android中View的扩展,能够在Activity的layout中实现显示网页,它不 ...