javascript - 我是否需要像 Java 一样同步 Node.js 代码?

标签 javascript node.js asynchronous

我最近才开始开发 Node.js,所以如果这是一个愚蠢的问题请原谅我 - 我来自 Javaland,那里的对象仍然快乐地按顺序和同步地生活。 ;)

我有一个 key 生成器对象,它使用高低算法的变体发出数据库插入的 key 。这是我的代码:

function KeyGenerator() {
    var nextKey;
    var upperBound;

    this.generateKey = function(table, done) {
        if (nextKey > upperBound) {
            require("../sync/key-series-request").requestKeys(function(err,nextKey,upperBound) {
                if (err) { return done(err); }
                this.nextKey = nextKey;
                this.upperBound = upperBound;
                done(nextKey++);
            });
        } else {
            done(nextKey++);
        }
    }
}

显然,当我要求它提供 key 时,我必须确保它永远不会两次发出相同的 key 。在 Java 中,如果我想启用并发访问,我会使其同步

  1. 在node.js中,是否有类似的概念,或者没有必要?我打算向生成器请求一堆 key ,以便使用 async.parallel 进行批量插入。我的期望是,由于 Node 是单线程的,我不必担心同一 key 多次发出,有人可以确认这是正确的吗?

  2. 获取一个新系列涉及异步数据库操作,因此如果我同时发出 20 个 key 请求,但该系列只剩下两个 key ,那么我最终不会得到 18 个新系列请求吗?我该怎么做才能避免这种情况?

更新 这是 requestKeys 的代码:

exports.requestKeys = function (done) {
    var db = require("../storage/db");
    db.query("select next_key, upper_bound from key_generation where type='issue'", function(err,results) {
        if (err) { done(err); } else {
            if (results.length === 0) {
            // Somehow we lost the "issue" row - this should never have happened
                done (new Error("Could not find 'issue' row in key generation table"));
            } else {
                var nextKey = results[0].next_key;
                var upperBound = results[0].upper_bound;
                db.query("update key_generation set next_key=?, upper_bound=? where type='issue'",
                    [ nextKey + KEY_SERIES_WIDTH, upperBound + KEY_SERIES_WIDTH],
                    function (err,results) {
                        if (err) { done(err); } else {
                            done(null, nextKey, upperBound);
                    }
                });
            }
        }
    });
}

更新2

我可能应该提到,即使不必请求新系列,使用 key 也需要数据库访问,因为使用的 key 必须标记为在数据库中使用。代码没有反射(reflect)这一点,因为我在开始实现该部分之前遇到了麻烦。

更新3

我想我是通过事件发射得到的:

function KeyGenerator() {
    var nextKey;
    var upperBound;
    var emitter = new events.EventEmitter();
    var requesting = true;

    // Initialize the generator with the stored values
    db.query("select * from key_generation where type='use'", function(err, results)
        if (err) { throw err; }
            if (results.length === 0) {
                throw new Error("Could not get key generation parameters: Row is missing");
            }
            nextKey = results[0].next_key;
            upperBound = results[0].upper_bound;
            console.log("Setting requesting = false, emitting event");
            requesting = false;
            emitter.emit("KeysAvailable");
    });

    this.generateKey = function(table, done) {
        console.log("generateKey, state is:\n    nextKey: " + nextKey + "\n    upperBound:" + upperBound + "\n    requesting:" + requesting + " ");
        if (nextKey > upperBound) {
            if (!requesting) {
                requesting = true;
                console.log("Requesting new series");
                require("../sync/key-series-request").requestSeries(function(err,newNextKey,newUpperBound) {
                    if (err) { return done(err); }
                    console.log("New series available:\n    nextKey: " + newNextKey + "\n    upperBound: " + newUpperBound);
                    nextKey = newNextKey;
                    upperBound = newUpperBound;
                    requesting = false;
                    emitter.emit("KeysAvailable");
                    done(null,nextKey++);
                });
            } else {
                console.log("Key request is already underway, deferring");
                var that = this;
                emitter.once("KeysAvailable", function() { console.log("Executing deferred call"); that.generateKey(table,done); });
            }
        } else {
            done(null,nextKey++);
        }
    }
}

我已经用日志输出填充了它,它确实做了我想要的事情。

最佳答案

正如另一个答案提到的,您最终可能会得到与您想要的不同的结果。按顺序排列事情:

function KeyGenerator() {
    // at first I was thinking you wanted these as 'class' properties
    // and thus would want to proceed them with this. rather than as vars
    // but I think you want them as 'private' members variables of the
    // class instance. That's dandy, you'll just want to do things differently
    // down below
    var nextKey;
    var upperBound;

    this.generateKey = function (table, done) {
        if (nextKey > upperBound) {
            // truncated the require path below for readability.
            // more importantly, renamed parameters to function
            require("key-series-request").requestKeys(function(err,nKey,uBound) {
                if (err) { return done(err); }
                // note that thanks to the miracle of closures, you have access to
                // the nextKey and upperBound variables from the enclosing scope
                // but I needed to rename the parameters or else they would shadow/
                // obscure the variables with the same name.
                nextKey = nKey;
                upperBound = uBound;
                done(nextKey++);
            });
        } else {
            done(nextKey++);
        }
    }
}

关于.requestKeys函数,您将需要以某种方式引入某种同步。从某种意义上来说,这实际上并不可怕,因为只有一个执行线程,您不需要在单个操作中设置信号量的挑战,但处理多个调用者是具有挑战性的,因为您将需要其他调用者调用者有效(但不是真正)阻止等待第一次调用 requestKeys(),该调用将返回数据库。

我需要多考虑一下这部分。我心中有一个基本的解决方案,其中涉及设置一个简单的信号量并对回调进行排队,但是当我输入它时,我意识到在处理排队的回调时我实际上引入了一个更微妙的潜在同步错误。

更新:

当您撰写有关 EventEmitter 方法的文章时,我刚刚完成了一种方法,这似乎很合理。请参阅this gist这说明了该方法。我拿了。只需运行它,您就会看到该行为。它有一些控制台日志记录,可以查看哪些调用因新的关键 block 而被推迟,或者哪些调用可以立即处理。该解决方案的主要移动部分是(请注意,keyManager 提供了 require('key-series-request') 的 stub 实现:

function KeyGenerator(km) {
    this.nextKey = undefined;
    this.upperBound = undefined;
    this.imWorkingOnIt = false;
    this.queuedCallbacks = [];
    this.keyManager = km;


    this.generateKey = function(table, done) {
        if (this.imWorkingOnIt){
            this.queuedCallbacks.push(done);
            console.log('KG deferred call. Pending CBs: '+this.queuedCallbacks.length);
            return;
        };
        var self=this;
        if ((typeof(this.nextKey) ==='undefined') || (this.nextKey > this.upperBound) ){
            //  set a semaphore & add the callback to the queued callback list
            this.imWorkingOnIt = true;
            this.queuedCallbacks.push(done);
            this.keyManager.requestKeys(function(err,nKey,uBound) {
                if (err) { return done(err); }
                self.nextKey = nKey;
                self.upperBound = uBound;
                var theCallbackList = self.queuedCallbacks;
                self.queuedCallbacks = [];
                self.imWorkingOnIt = false;
                theCallbackList.forEach(function(f){
                    // rather than making the final callback directly,
                    // call KeyGenerator.generateKey() with the original
                    // callback
                    setImmediate(function(){self.generateKey(table,f);});

                });

            });
        } else {
            console.log('KG immediate call',self.nextKey);
            var z= self.nextKey++;
            setImmediate(function(){done(z);});
        }
    }
};

关于javascript - 我是否需要像 Java 一样同步 Node.js 代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22731951/

相关文章:

javascript - JavaScript 中的反向异或运算符?

javascript - Javascript 正则表达式中的 g 和 m 修饰符有什么区别?

javascript - 如何将元素中的对象引用存储为自定义属性?

node.js - iisnode 不会运行 Express

javascript - 嵌套的 Promise.all 函数产生意外的结果

javascript - Redux 不会重新渲染组件

node.js - Node 速度慢且对大数据文件无响应

node.js - Node Express 测试模拟 res.status(status).json(obj)

iphone - NSURLConnection 用于异步 Web 服务调用的 NSURLRequest 代理

javascript - Node.JS : forEach vs for loop Asynchronous nature