流星和Fibers/bindEnvironment()是怎么回事? [英] What's going on with Meteor and Fibers/bindEnvironment()?

查看:97
本文介绍了流星和Fibers/bindEnvironment()是怎么回事?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用Fibers/Meteor.bindEnvironment()时遇到困难.如果集合开始为空,我试图进行代码更新并插入到集合中.所有这些都应该在启动时在服务器端运行.

 function insertRecords() {
  console.log("inserting...");
  var client = Knox.createClient({
    key: apikey,
    secret: secret,
    bucket: 'profile-testing'
  });
  console.log("created client");
  client.list({ prefix: 'projects' }, function(err, data) {
    if (err) {
      console.log("Error in insertRecords");
    }

    for (var i = 0; i < data.Contents.length; i++)  {
      console.log(data.Contents[i].Key);
      if (data.Contents[i].Key.split('/').pop() == "") {
        Projects.insert({ name: data.Contents[i].Key, contents: [] });
      } else if (data.Contents[i].Key.split('.').pop() == "jpg") {
        Projects.update( { name: data.Contents[i].Key.substr(0,
                           data.Contents[i].Key.lastIndexOf('.')) },
                         { $push: {contents: data.Contents[i].Key}} );
      } else {
        console.log(data.Contents[i].Key.split('.').pop());
      }
    }      
  });
}

if (Meteor.isServer) {
  Meteor.startup(function () {
    if (Projects.find().count() === 0) {
      boundInsert = Meteor.bindEnvironment(insertRecords, function(err) {
        if (err) {
          console.log("error binding?");
          console.log(err);
        }
      });
      boundInsert();
    }
  });
}
 

我第一次写这篇文章时,遇到了一些错误,需要将回调包装在Fiber()块中,然后在IRC上进行讨论,有人建议尝试使用Meteor.bindEnvironment(),因为那应该将其放入Fiber中.那是行不通的(我看到的唯一输出是inserting...,这意味着bindEnvironment()不会引发错误,但是它也不会在块内运行任何代码).然后我明白了.我现在的错误是:Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.

我是Node的新手,并不完全了解Fibers的概念.我的理解是,它们类似于使用线程的C/C ++/每种语言中的线程,但是我不理解扩展到服务器端代码的含义是/为什么我的代码在尝试插入时会引发错误.一个集合.谁能向我解释一下?

谢谢.

解决方案

您使用的bindEnvironment略有错误.因为它的使用位置已经在光纤中,而离开Knox客户端的回调不再在光纤中.

bindEnvironment有两个用例(我可以想到,可能还有更多用例!):

  • 您有一个必须更改的全局变量,但您不希望它影响其他用户的会话

  • 您正在使用第三方api/npm模块管理回调(情况似乎如此)

Meteor.bindEnvironment创建一个新的光纤,并将当前光纤的变量和环境复制到新的光纤.需要的重点是当您使用nom模块的方法回调时.

幸运的是,还有另一种方法可以处理等待您的回调并将该回调绑定到名为Meteor.wrapAsync的光纤中.

所以您可以这样做:

您的启动功能已经具有光纤并且没有回调,因此您在这里不需要bindEnvironment.

Meteor.startup(function () {
   if (Projects.find().count() === 0) {
     insertRecords();
   }
});

您的插入记录功能(使用wrapAsync)使您不需要回调

function insertRecords() {
  console.log("inserting...");
  var client = Knox.createClient({
    key: apikey,
    secret: secret,
    bucket: 'profile-testing'
  });

  client.listSync = Meteor.wrapAsync(client.list.bind(client));

  console.log("created client");

  try {
      var data = client.listSync({ prefix: 'projects' });
  }
  catch(e) {
      console.log(e);
  }    

  if(!data) return;


  for (var i = 1; i < data.Contents.length; i++)  {
    console.log(data.Contents[i].Key);
    if (data.Contents[i].Key.split('/').pop() == "") {
      Projects.insert({ name: data.Contents[i].Key, contents: [] });
    } else if (data.Contents[i].Key.split('.').pop() == "jpg") {
      Projects.update( { name: data.Contents[i].Key.substr(0,
                         data.Contents[i].Key.lastIndexOf('.')) },
                       { $push: {contents: data.Contents[i].Key}} );
    } else {
      console.log(data.Contents[i].Key.split('.').pop());
    }
  }      
});

需要牢记的几件事.纤维不像螺纹. NodeJS中只有一个线程.

纤维更像是可以同时运行的事件,但是如果存在等待类型的情况(例如,从Internet下载文件),则不会互相阻塞.

因此,您可以拥有同步代码,而不会阻止其他用户的事件.它们轮流运行,但仍在单个线程中运行.因此,这就是Meteor在服务器端具有同步代码的方式,可以等待内容,但是其他用户不会因此而受阻,并且可以执行内容,因为他们的代码在不同的光纤中运行.

Chris Mather在 http://eventedmind.com

上有几篇很好的文章.

Meteor.wrapAsync做什么?

Meteor.wrapAsync接受您提供的方法作为第一个参数,并在当前光纤中运行它.

它还会附加一个回调(假定该方法采用的最后一个参数具有回调,其中第一个参数是错误,第二个参数是错误,例如function(err,result).

回调与Meteor.bindEnvironment绑定,并阻塞当前的Fiber,直到触发回调为止.回调触发后,它将立即返回result或引发err.

因此将异步代码转换为同步代码非常方便,因为您可以在下一行使用方法的结果,而不是使用回调和嵌套更深层的函数.它还可以为您解决bindEnvironment的问题,因此您不必担心丢失光纤的作用域.

更新 Meteor._wrapAsync现在已Meteor.wrapAsync,并且已记录. >

I am having difficulty using Fibers/Meteor.bindEnvironment(). I tried to have code updating and inserting to a collection if the collection starts empty. This is all supposed to be running server-side on startup.

function insertRecords() {
  console.log("inserting...");
  var client = Knox.createClient({
    key: apikey,
    secret: secret,
    bucket: 'profile-testing'
  });
  console.log("created client");
  client.list({ prefix: 'projects' }, function(err, data) {
    if (err) {
      console.log("Error in insertRecords");
    }

    for (var i = 0; i < data.Contents.length; i++)  {
      console.log(data.Contents[i].Key);
      if (data.Contents[i].Key.split('/').pop() == "") {
        Projects.insert({ name: data.Contents[i].Key, contents: [] });
      } else if (data.Contents[i].Key.split('.').pop() == "jpg") {
        Projects.update( { name: data.Contents[i].Key.substr(0,
                           data.Contents[i].Key.lastIndexOf('.')) },
                         { $push: {contents: data.Contents[i].Key}} );
      } else {
        console.log(data.Contents[i].Key.split('.').pop());
      }
    }      
  });
}

if (Meteor.isServer) {
  Meteor.startup(function () {
    if (Projects.find().count() === 0) {
      boundInsert = Meteor.bindEnvironment(insertRecords, function(err) {
        if (err) {
          console.log("error binding?");
          console.log(err);
        }
      });
      boundInsert();
    }
  });
}

My first time writing this, I got errors that I needed to wrap my callbacks in a Fiber() block, then on discussion on IRC someone recommending trying Meteor.bindEnvironment() instead, since that should be putting it in a Fiber. That didn't work (the only output I saw was inserting..., meaning that bindEnvironment() didn't throw an error, but it also doesn't run any of the code inside of the block). Then I got to this. My error now is: Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.

I am new to Node and don't completely understand the concept of Fibers. My understanding is that they're analogous to threads in C/C++/every language with threading, but I don't understand what the implications extending to my server-side code are/why my code is throwing an error when trying to insert to a collection. Can anyone explain this to me?

Thank you.

解决方案

You're using bindEnvironment slightly incorrectly. Because where its being used is already in a fiber and the callback that comes off the Knox client isn't in a fiber anymore.

There are two use cases of bindEnvironment (that i can think of, there could be more!):

  • You have a global variable that has to be altered but you don't want it to affect other user's sessions

  • You are managing a callback using a third party api/npm module (which looks to be the case)

Meteor.bindEnvironment creates a new Fiber and copies the current Fiber's variables and environment to the new Fiber. The point you need this is when you use your nom module's method callback.

Luckily there is an alternative that takes care of the callback waiting for you and binds the callback in a fiber called Meteor.wrapAsync.

So you could do this:

Your startup function already has a fiber and no callback so you don't need bindEnvironment here.

Meteor.startup(function () {
   if (Projects.find().count() === 0) {
     insertRecords();
   }
});

And your insert records function (using wrapAsync) so you don't need a callback

function insertRecords() {
  console.log("inserting...");
  var client = Knox.createClient({
    key: apikey,
    secret: secret,
    bucket: 'profile-testing'
  });

  client.listSync = Meteor.wrapAsync(client.list.bind(client));

  console.log("created client");

  try {
      var data = client.listSync({ prefix: 'projects' });
  }
  catch(e) {
      console.log(e);
  }    

  if(!data) return;


  for (var i = 1; i < data.Contents.length; i++)  {
    console.log(data.Contents[i].Key);
    if (data.Contents[i].Key.split('/').pop() == "") {
      Projects.insert({ name: data.Contents[i].Key, contents: [] });
    } else if (data.Contents[i].Key.split('.').pop() == "jpg") {
      Projects.update( { name: data.Contents[i].Key.substr(0,
                         data.Contents[i].Key.lastIndexOf('.')) },
                       { $push: {contents: data.Contents[i].Key}} );
    } else {
      console.log(data.Contents[i].Key.split('.').pop());
    }
  }      
});

A couple of things to keep in mind. Fibers aren't like threads. There is only a single thread in NodeJS.

Fibers are more like events that can run at the same time but without blocking each other if there is a waiting type scenario (e.g downloading a file from the internet).

So you can have synchronous code and not block the other user's events. They take turns to run but still run in a single thread. So this is how Meteor has synchronous code on the server side, that can wait for stuff, yet other user's won't be blocked by this and can do stuff because their code runs in a different fiber.

Chris Mather has a couple of good articles on this on http://eventedmind.com

What does Meteor.wrapAsync do?

Meteor.wrapAsync takes in the method you give it as the first parameter and runs it in the current fiber.

It also attaches a callback to it (it assumes the method takes a last param that has a callback where the first param is an error and the second the result such as function(err,result).

The callback is bound with Meteor.bindEnvironment and blocks the current Fiber until the callback is fired. As soon as the callback fires it returns the result or throws the err.

So it's very handy for converting asynchronous code into synchronous code since you can use the result of the method on the next line instead of using a callback and nesting deeper functions. It also takes care of the bindEnvironment for you so you don't have to worry about losing your fiber's scope.

Update Meteor._wrapAsync is now Meteor.wrapAsync and documented.

这篇关于流星和Fibers/bindEnvironment()是怎么回事?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆