很多人在用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. capitalize()在Python中含义

    Python为string对象提供了转换大小写的方法:upper() 和 lower(). 还不止这些,Python还为我们提供了首字母大写,其余小写的capitalize()方法, 以及所有单词首字 ...

  2. CAS实现单点登录方案(SSO完整版)

    一.简介 1.cas是由耶鲁大学研发的单点登录服务器 2.本教材所用环境 Tomcat7.2 JDK1.7 CAS Service 版本    cas-server-3.4.8-release CAS ...

  3. [转贴]实践:C++平台迁移以及如何用C#做C++包装层

    终于有个C++ 如何调用C#类库的文章,收藏之 在前面,我们看过OpenTK与MOgre,这二个项目都是C#项目,但是他的实现都是C++.他们简单来说就是一个包装层.常见的包装方式有二种,一 种就是我 ...

  4. ARC模式下的单例写法

    // 单例 + (id)sharedInstance { __strong static id sharedObject = nil; static dispatch_once_t onceToken ...

  5. Android Volley - volley StringRequest編碼問題

    有些時候這個類並不能很好的解決中文編碼問題 如果出現亂碼,就 要重寫該類的parseNetworkResponse 方法了. 繼承StringRequest,然後重寫parseNetworkRespo ...

  6. sqrt (x) 牛顿迭代法

    参考: 0开方 是 0 1的开方式 1 2的开方式 1.4 3.的开方=(1.4+3/1.4)/2 牛顿迭代法:学习自 http://blog.csdn.net/youwuwei2012/articl ...

  7. Hadoop-Map/Reduce之单表连接的实现

    MapReduce程序就是根据其特性对数据进行一个简单的逻辑处理,其中最为重要的一个特性就是根据key值将value值进行合并,其次就是在shuffle阶段有排序. 遇到一个MR程序就是要巧妙利用合并 ...

  8. 4 weekend110的hive入门

    查看企业公认的最新稳定版本:       https://archive.apache.org/dist/  Hive和HBase都很重要,当然啦,各自也有自己的替代品. 在公司里,SQL有局限,大部 ...

  9. Why should i use url.openStream instead of of url.getContent?

    I would like to retrieve the content of a url. Similar to pythons: html_content = urllib.urlopen(&qu ...

  10. 什么时候使用Shell

    因为Shell似乎是各UNIX系统之间通用的功能,并且经过了POSIX的标准化.因此,Shell脚本只要“用心写”一次,即可应用到很多系统上.因此,之所以要使用Shell脚本是基于: 简单性:Shel ...