使用NPM Package和Meteor.wrapAsync的异步问题

时间:2017-05-01 02:08:05

标签: javascript node.js meteor

当使用来自npm模块(client.call)的函数node-celery时,似乎没有执行client.call的回调函数。

Meteor.methods({
    'estimates.request'(data) {
        var celery = require('node-celery')
        var client = celery.createClient({...})

        client.on('connect', function() {
            console.log('connected');                   // this is executed
            client.call('proj.tasks.register', [name],
                function(err, result) {
                    console.log('result: ', result);    // this is not executed
                    client.end();
                    return result;
                }
            );
        });
    }
});

client.call中尝试包裹Meteor.wrapAsync

callFunc = client.on('connect', function() {
    console.log('connected');                   // this is executed
    client.call('proj.tasks.register', [name],
        function(err, result) {
            console.log('result: ', result);    // this is not executed
            client.end();
            return result;
        }
    );
});

callFuncSync = Meteor.wrapAsync(callFunc)
callFuncSync()

但是这会在Meteor服务器控制台中引发错误:

err:  [Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.]
err:  { [Error: read ECONNRESET] code: 'ECONNRESET', errno: 'ECONNRESET', syscall: 'read' }

问题:我们应该如何使用Meteor.bindEnvironment来解决此问题?

2 个答案:

答案 0 :(得分:3)

来自文档,

  

包装一个以回调函数作为最终参数的函数。包装函数的回调签名应为function(error, result){}

您的代码只包装事件附件调用的返回值。

你可以包装整个事物(连接+任务调用),但在你的情况下,我建议采用不同的方法:

现在,每当有人调用该方法时,您都会连接到Celery。如果可能的话,我建议与Celery建立持久的联系。

您尝试换行的函数不符合wrapAsync要求,因此您必须将它们包装在一个函数中。

在以下代码中,连接和调用功能都得到了处理。请注意,这些函数采用cb参数,将由Meteor提供给它们,并根据需要使用错误和/或结果调用它。

然后将这些函数传递给wrapAsync

如果错误传递给回调并且使用光纤模拟同步运行(如果它们以同步方式调用)(即,没有传递回调),则会抛出它们的同步版本。这就是try..catch阻止的原因。

import { Meteor } from 'meteor/meteor';
const celery = require('node-celery'); // or `import` it

function createAndConnectAsync(details, cb) {
    const client = celery.createClient(details);
    const errHandler = function(e) {
        cb(e, client);
    };
    client.once('connect', function() {
        client.off('error', errHandler);
        cb(null, client); // success
    });
    client.once('error', errHandler);
}

function callAsync(client, task, args, cb) {
    client.call(task, args, function(result) {
       cb(null, result);
    });
}

const createAndConnect = Meteor.wrapAsync(createAndConnectAsync);
const call = Meteor.wrapAsync(callAsync);

let client;
try {
   client = createAndConnect({...}); // returns after the client is connected
} catch(e) {
    // connection error
}

Meteor.methods({
    'estimates.request'(data) {
        // generate `name`
        const result = call(client, 'proj.tasks.register', [name]);
        return result;
    }
});

答案 1 :(得分:2)

组合异步库可能很棘手,通常它们有一些辅助方法,如Meteor的bindEnvironment

  

从Meteor.bindEnvironment返回的函数也会自动在光纤中运行。

Meteor.methods({
    'estimates.request'(data) {
        var celery = require('node-celery')
        var client = celery.createClient({...})

        var otherCallback = Meteor.bindEnvironment(function(err, result) {
            console.log('result: ', result);    // this is not executed
            client.end();
            return result;
        });
        var connectCallback = Meteor.bindEnvironment(function() {
          console.log('connected');                   // this is executed
          client.call('proj.tasks.register', [name], otherCallback);
        });
        client.on('connect', connectCallback);
    }
});

仅供参考:未对此进行测试,因为您的示例并非100%完成:)