【问题标题】:Wrapping MongoDB calls within a Promise在 Promise 中包装 MongoDB 调用
【发布时间】:2015-05-13 20:08:36
【问题描述】:

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

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

我提出这个问题的唯一一点是节点调用当然是异步的。所以我选择使用 node 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() 函数上放置 Fiber(未来)的原因是,当我将函数 process_1() 放置在 Q 链的顶部时,我得到了错误:Can't wait without a fiber] .

如果我在顶部的 Q 链中删除 process_1() 并从 sub_process_1() 中删除 .future() ,则不会引发异常。

问题

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

【问题讨论】:

  • 我认为你可以做任何你想做的事情,只需确保将所有异步回调包装在 Meteor.bindEnvironment() 中,因此可能会显示路由和文件代码。除非您尝试使回调链中的某些任务并发,否则您可能不需要单独使用 Fiber 或 Promise。
  • 我似乎通过在上面的函数末尾挂上一个 .future() 来让它工作。我今天要结束了,但明天我会发布更多代码。我认为 bindEnviornment (bE) 只是一个包含一些额外 Meteor 信息的 Fiber 的包装器——所以通过使用 bE,我们毕竟不是在使用 Fibers 吗?此外,未来只是纤维上的糖(这是我正在使用的)。我认为问题在于,Mongo 调用被视为 I/O,由操作系统处理并且是异步的。因此,由于我在 Meteor 环境之外调用数据库而不是在幕后包裹在 Fiber 中,所以它在抱怨。
  • get_record_1_async() 中的异步调用是什么?我认为您错误地假设像 findOne() 这样的流星 mongo 调用是异步的,并添加了很多不必要的代码。
  • 关于 get_record_1_async( ) 函数的异步除了它的名称和我正在呼唤 Mongo 的事实之外,还有一点值得注意。我可以删除名称并删除调用方法 sub_process_1( ) 上的 .future() ,我会得到 [错误:没有光纤无法等待]...但这仅在我放置 process_1() 时发生在顶部的 Q 链中。
  • 我将编辑我的最后一个示例以更改函数的名称并解释为什么我正在做我正在做的更好一点。

标签: javascript mongodb meteor promise node-fibers


【解决方案1】:

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

如果您使用 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();
  })
);

除非您试图让多个异步事件同时运行,否则您自己没有必要使用纤程或 Promise。

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

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

【讨论】:

    猜你喜欢
    • 2021-09-14
    • 2014-05-10
    • 1970-01-01
    • 1970-01-01
    • 2023-03-31
    • 2017-01-30
    • 2015-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多