【问题标题】:Prevent double function calling by parameter防止通过参数调用双重函数
【发布时间】:2014-02-14 21:58:23
【问题描述】:

我的 Node.js 应用程序中有一个循环,它不断地从我的数据库中获取“未处理”的行(每秒 1 个请求)。

我从数据库收到的每一行都将调用一个具有唯一数字 ID 的函数,并将数据库中的其他详细信息作为参数调用。一旦在此函数中处理了数据,数据库就会更新,并且该行被标记为“已处理”。

function fetch() {
  db.query("SELECT id, data FROM table WHERE processed=0 ORDER BY id ASC", function(err, results) {
    if(results.length > 0) {
        for(var i=0; i<results.length; i++)
            process(results[i].id, results[i].data);
    }
  });
}
var interval_fetch = setInterval(fetch, 1000);

function process(id, data) {
  // Process data
  db.query("UPDATE table SET processed=1 WHERE id="+id);
}

但是,在某些情况下,处理数据和更新数据库需要一秒钟以上的时间。在这种情况下 process() 它使用相同的参数调用了两次甚至更多次。

在 Node.js 环境中,什么是最简单的方法来确保函数只在某个 ID 参数的情况下同时被调用一次?

是否有任何软件包提供此功能并且只需要两三行额外代码?

(解决方案不一定要防止多次调用该函数。如果我能够检查进程()内部,如果它已经用某个id调用,我也可以在数据之前结束它被处理了两次。)

【问题讨论】:

    标签: javascript node.js function unique


    【解决方案1】:

    是否有任何软件包提供此功能并且只需要两三行额外代码?

    是的:https://github.com/isaacs/once

    此外,所有 Promise 库都应开箱即用地提供此功能。还有发电机。

    但我建议您更改编码方式,而不是使用它们。 setTimeout 而不是 setInterval 可以通过这种方式消除整个问题:

    function fetch() {
      db.query("SELECT id, data FROM table WHERE processed=0 ORDER BY id ASC", function(err, results) {
        if(results.length > 0) {
           for(var i=0; i<results.length; i++)
              process(results[i].id, results[i].data)
        }
        setTimeout(fetch, 1000)
      })
    }
    var interval_fetch = setTimeout(fetch, 1000)
    
    function process(id, data) {
      // Process data
      db.query("UPDATE table SET processed=1 WHERE id="+id)
    }
    

    【讨论】:

    • 我相信setInterval 的意图是因为他希望工作每秒重复一次,而不是因为他只希望它发生一次。
    • 这就是我在那里添加递归setTimeout 调用的原因
    • 感谢您的回复。该软件包部分执行了我想要的操作,但 setTimeout 建议没有。请参阅我自己的答案。
    【解决方案2】:

    简化,设置作业完成后的超时时间。

    function fetch() {
      db.query("SELECT id, data FROM table WHERE processed=0 ORDER BY id ASC", function(err, results) {
        if(results.length > 0) {
            for(var i=0; i<results.length; i++)
                process(results[i].id, results[i].data);
        }
      });
    }
    setTimeout(fetch, 1000);
    
    function process(id, data) {
      // Process data
      db.query("UPDATE table SET processed=1 WHERE id="+id, function() {
        setTimeout(fetch, 1000);
      });
    
    }
    

    【讨论】:

    • 感谢您的回答。不幸的是,这并不像我想的那样工作。如果查询返回具有不同数据的多行,则使用不同的参数多次调用 process()。一旦完成,每个人都会设置一个新的超时。如果一个函数调用在 0.1 秒后完成,另一个函数调用在 5 秒后完成,那么慢的会被再次处理。
    【解决方案3】:

    今天早上我得到了一个相对简单的解决方案。因此,我正在回答我自己的问题。

    我只是维护一个对象,其中包含当前正在处理但尚未在数据库中更新的所有 ID。每次调用 process() 时,我都会检查 ID 当前是否在进行中,如果适用则取消,然后数据将被处理两次。

    var in_progress = {}; // Object that contains the IDs
    
    function fetch() {
      db.query("SELECT id, data FROM table WHERE processed=0 ORDER BY id ASC", function(err, results) {
        if(results.length > 0) {
            for(var i=0; i<results.length; i++)
                process(results[i].id, results[i].data);
        }
      });
    }
    var interval_fetch = setInterval(fetch, 1000);
    
    function process(id, data) {
      if(in_progress.hasOwnProperty(id)) { // Check if ID is in progress
        console.log("ID "+id+" in progress - do nothing");
        return;
      } else {
        in_progress[id] = true; // Insert ID into object
    
        // Process data here, then run below query
    
        db.query("UPDATE table SET processed=1 WHERE id="+id, function() {
          // Remove ID from object once DB is updated - Timeout to make sure nothing can overlap with the fetch function
          setTimeout(function(){ delete in_progress[id]; }, 1000); 
        });
      }
    }
    

    这完全符合我的要求。我什至可以做出更快的获取间隔,开始处理我的数据库中的所有新数据,而不会出现明显的延迟。仍然没有任何东西会被处理两次。

    【讨论】:

      猜你喜欢
      • 2019-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-14
      • 1970-01-01
      • 2017-02-15
      • 1970-01-01
      相关资源
      最近更新 更多