在Promise中包装MongoDB调用

时间:2015-03-11 20:30:50

标签: javascript mongodb meteor promise node-fibers

我一般使用Meteor(1.0.3),但是对于一个特殊情况,我使用原始服务器端路由来渲染文件 - 所以我不在Meteor方法之外。

我正在使用节点fs.writeFile / fs.readFile和exec命令来调用Linux命令行实用程序。

我唯一能说明的是节点调用当然是异步的。所以我选择使用节点Q库来管理异步回调。

这一直都有用,直到我添加一行来调用MongoDB数据库。

这样的电话:

var record_name = Mongo_Collection_Name.findOne({_personId: userId}, {fields: {'_id': 0}});

产生以下错误:

  

[错误:没有纤维就等不及了]

只有当我将函数包装在Promise中时才会出现错误。

例如,像这样的东西会抛出:

getRecordExample = function () {
  var deferred = Q.defer();
  var record_name = Mongo_Collection_Name.findOne({_personId: userId}, {fields: {'_id': 0}});

  // do something

  // if no error
  deferred.resolve(record_name);

  return deferred.promise;
}

如果我使用Meteor Fibers库,我不会收到错误:

getRecordExample = function () {
  var deferred = Q.defer();
  Fiber = Npm.require('fibers');
  var record_name

  Fiber(function () {
    record_name = Mongo_Collection_Name.findOne({_personId: userId});
  }).run()
  // do something

  // if no error
  deferred.resolve(record_name);

  return deferred.promise;
}

但是,record_name变量在光纤外部是未定义的,因此我没有办法将变量传递到光纤范围之外。就我所知。

更准确的例子

这有点长,所以你必须向下滚动才能看到它。我基本上是在这里建立一个工作流程,所以有进程和子进程。

// both/routes.js
Router.route('/get-route', function(req, res) {
  // get the userId then start the workflow below

  // using Promises here because these were firing concurrently
  Q(userId)
  .then(process_1)
  .then(process_2)
  .done();
}, { name: 'server-side-ir-route', where: 'server' }

// server.js
process_1 = function (userId) {
  sub_process_1(userId);

  sub_process_2(userId);

  return userId;
}

process_2 = function (userId) {
  sub_process_3(userId);

  sub_process_4(userId);

  return userId;
}

sub_process_1 = function (userId) {
  var result = get_record_1(userId);

  // do stuff with result

  // using Q library to call out to async fs.writeFile, return Promise
  fs_writeFile_promise(result)
  .catch(function (error) {
    console.log('error in sub_process_1_write', error);
  })
  .done(function () {
    console.log('done with sub_process_1');
  }

  return userId;
}.future() // <-- if no future() here, the exception is thrown.

sub_process_2 = function (userId) {
  var result = get_record_2(userId);

  // do stuff with result

  // using Q library to call out to async fs.writeFile, return Promise
  fs_writeFile_promise(result)
  .catch(function (error) {
    console.log('error in sub_process_1_write', error);
  })
  .done(function () {
    console.log('done with sub_process_1');
  }

  return userId;
}.future()

// async because of I/O operation (I think)
get_record_1 = function (userId) {
  var record_1 = Mongo_Collection_Name.findOne({'userId': userId});
  // do stuff
  return record_1;
}
get_record_2 = function (userId) {
  var record_2 = Mongo_Collection_Name.findOne({'userId': userId});
  // do stuff
  return record_2;
}

// async operation using Q library to return a Promise
fs_writeFile_promise = function (obj) {
  var deferred = Q.defer();
  fs.writeFile(obj.file, obj.datas, function (err, result) {
    if (err) deferred.reject(err);
    else deferred.resolve('write data completed');
  });
  return deferred.promise;
}

现在,我们假设process_2函数与process_1

完全相同

另外,我们应该假设我在每个函数中都有console.log('step_start')和console.log('step_end')。这就是它在命令行上的样子:

  • 启动流程
  • 结束流程
  • 启动流程1
  • 结束流程1
  • 启动流程2
  • 启动子流程1
  • 获得记录1
  • 启动子流程2
  • 获得记录2
  • 返回记录1
  • 结束子流程1
  • 在子流程1中调用writeData
  • 返回记录2
  • 在子流程2中调用writeData
  • 结束流程2
  • 结束子流程1

我必须在sub_process_1()函数上放置光纤(未来)的原因是因为当我将函数process_1()放在顶部的Q链中时,我得到了错误:没有光纤就等不及了

如果我删除顶部Q链中的process_1()并从su​​b_process_1()中删除.future(),则不会抛出任何异常。

问题

  • 为什么在Promise中调用Mongo集合会导致a Meteor应用程序中的光纤错误?
  • 通常在同步函数中调用异步函数会导致同步函数成为异步函数吗?
  • 我该如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

解决此问题的最常用方法是使用Meteor.bindEnvironment()中的Meteor函数包装异步回调。

如果您使用Meteor核心WebApp package来处理服务器端路由,代码就像这样(也在meteorpad中):

WebApp.connectHandlers.use(
  '/test',
  Meteor.bindEnvironment(function(req, res, next) {
    var someSyncData = Players.findOne();
    res.write(JSON.stringify(someSyncData));
    res.end();
  })
);

除非您尝试同时运行多个异步事件,否则不必使用光纤或承诺。

为了处理文件读取或其他尚未同步的功能,Meteor还提供Meteor.wrapAsync()以使它们同步。

还有packages and a help page可以为您提供其他高级选择。