• 用NodeJs实现延迟调用,规避定时任务的闭包问题


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

  • 相关阅读:
    HDU 4819 Mosaic --二维线段树(树套树)
    Codeforces Round #274 Div.1 C Riding in a Lift --DP
    ZOJ 3829 Known Notation --贪心+找规律
    JAVA成员变量和静态变量的区别
    spring注解
    js中double类型的数据加减的时候出错
    js在页面间传值的方法记录
    将表中数据转换成java entity实例
    最近都写APP的接口,有苦说不出啊.
    写于2018年第一场雪----记我的第一年工作
  • 原文地址:https://www.cnblogs.com/alwu007/p/5418027.html
Copyright © 2020-2023  润新知