Meteor._wrapAsync导致Meteor无响应

Nyx*_*nyx 3 javascript node.js meteor

Meteor._wrapAsync用来强制只对一个函数writeMeLater执行一次调用.如果writeMeLater在1秒内进行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)
}
Run Code Online (Sandbox Code Playgroud)

输出:

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

使用备用版本writeMeLater,我遇到同样的问题:

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

sai*_*unt 5

TL; DR - 你的writeMeLater函数需要一个callback参数.


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

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

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

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

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

简化的克隆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();
    };
};
Run Code Online (Sandbox Code Playgroud)

有一个Future.wrap就是这样做的,Meteor._wrapAsync有点复杂,因为它通过使用来处理Meteor环境变量Meteor.bindEnvironment.

纤维和期货有点超出范围所以我不会深入研究它们,请务必查看有关该主题的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);
};
Run Code Online (Sandbox Code Playgroud)

你很高兴去!