Meteor._wrapAsync导致Meteor无响应

时间:2014-01-19 03:11:51

标签: javascript node.js meteor

我正在使用Meteor._wrapAsync强制只对函数writeMeLater进行一次调用 在任何时候执行。如果在1秒内拨打writeMeLater 10次,则其他9次呼叫应按顺序排队。

要检查writeMeLater是否同步运行,timestamp集合中的Logs字段应间隔1秒。

问题:使用以下代码,只执行对writeMeLater的第一次调用,其他9似乎不会运行。为什么会这样?

服务器代码:

writeMeLater = function(data) {
    console.log('writeMeLater: ', data)

    // simulate taking 1 second to complete
    Meteor.setTimeout(function() {
        Logs.insert({data: data, timestamp: new Date().getTime()})
    }, 1 * 1000)
}

writeMeLaterSync = Meteor._wrapAsync(writeMeLater)


// simulate calling the function many times real quick
for(var i=0; i<10; i++) {
    console.log('Loop: ', i)
    writeMeLaterSync(i)
}

输出:

=> Meteor server running on: http://localhost:4000/
I20140119-11:04:17.300(8)? Loop:  0
I20140119-11:04:17.394(8)? writeMeLater:  0

使用writeMeLater的替代版本,我遇到了同样的问题:

writeMeLater = function(data) {
    console.log('writeMeLater: ', data)
    setTimeout(Meteor.bindEnvironment( function() {
        Logs.insert({data: data, timestamp: new Date().getTime()})
    }), 1 * 1000)
}

1 个答案:

答案 0 :(得分:5)

TL; DR - 您的writeMeLater函数需要采用callback参数。


NodeJS经典异步函数通常具有以下签名:

function async(params..., callback) {
    try {
        var result = compute(params);
        callback(null,result);
    }
    catch {
        callback("something went wrong", null);
    }
}

它们接受任意数量的参数,最后一个是在计算准备就绪时运行的回调,使用2个参数调用:error如果一切正常则为null,result当然。

期望

Meteor._wrapAsync获得具有此签名的函数以返回新的伪同步函数。 Meteor“同步”函数允许您以同步方式编写代码,但它们并不像NodeJS fs.readFileSync那样真正同步,它会阻塞事件循环直到它完成(通常这很糟糕,除非你写的是命令行应用程序,这与Meteor不同。)

注意:使用NodeJS fs * Meteor中的同步功能很糟糕,因为你可能会被认为是“流星同步”,但事实并非如此,它们会阻止你的整个节点进程直到它们完成!你应该使用用Meteor._wrapAsync包装的fs异步func。

Meteor._wrapAsync的简化克隆如下所示:

var wrapAsync=function(asyncFunc) {
    // return a function who appears to run synchronously thanks to fibers/future
    return function() {
        var future = new Future();
        // take the arguments...
        var args = arguments;
        // ...and append our callback at the end
        Array.prototype.push.call(args, function(error, result) {
            if (error) {
                throw error;
            }
            // our callback calls future.return which unblocks future.wait
            future.return(result);
        });
        // call the async func with computed args
        asyncFunc.apply(null, args);
        // wait until future.return is called
        return future.wait();
    };
};

有一个Future.wrap正是如此,Meteor._wrapAsync有点复杂,因为它使用Meteor.bindEnvironment来处理Meteor环境变量。

纤维和期货有点超出范围所以我不会深入研究它们,请务必查看有关该主题的eventedmind.com视频。

介绍光纤 - https://www.eventedmind.com/feed/BmG9WmSsdzChk8Pye

使用期货 - https://www.eventedmind.com/feed/kXR6nWTKNctKariSY

Meteor._wrapAsync - https://www.eventedmind.com/feed/Ww3rQrHJo8FLgK7FF

现在你已经了解了如何在Meteor中封装异步函数,让我们修复你的代码。

如果异步函数没有将回调作为最后一个参数,它就不会调用它(显然),并且我们在包装函数中传递给它的回调也不会触发,这意味着future.return赢了不会被调用,这就是为什么你的程序首先被阻止的原因!

您只需重写writeMeLater即可将回调作为最终参数:

var writeMeLater = function(data, callback){
    console.log('writeMeLater: ', data);
    // simulate taking 1 second to complete
    Meteor.setTimeout(function() {
        Logs.insert({
            data:data,
            timestamp:new Date().getTime()
        });
        callback(null, "done processing " + data);
    }, 1 * 1000);
};

你很高兴去!