Что происходит с Meteor и 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 и не совсем понимаю концепцию волокон. Я понимаю, что они аналогичны потокам на C/С++/на каждом языке с потоковой обработкой, но я не понимаю, какие последствия распространяются на мой серверный код: почему мой код бросает ошибку при попытке вставить в Коллекция. Может кто-нибудь объяснить это мне?

Спасибо.

Ответ 1

Вы неправильно используете bindEnvironment. Потому что, когда его используют уже в волокне, и обратный вызов, который приходит от клиента Knox, больше не находится в волокне.

Есть два случая использования bindEnvironment (о которых я могу думать, их может быть больше!):

  • У вас есть глобальная переменная, которая должна быть изменена, но вы не хотите, чтобы она влияла на другие сеансы пользователя.

  • Вы управляете обратным вызовом с использованием стороннего модуля api/npm (что выглядит так)

Meteor.bindEnvironment создает новое волокно и копирует текущие переменные волокна и среду в новое волокно. Точка, в которой вы нуждаетесь, - это когда вы используете обратный вызов метода номера модуля.

К счастью, есть альтернатива, которая заботится о обратном вызове, ожидающем вас, и связывает обратный вызов в волокне с именем 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 существует только один поток.

Волокна больше похожи на события, которые могут запускаться в одно и то же время, но не блокируя друг друга, если существует сценарий типа ожидания (например, загрузка файла из Интернета).

Таким образом, вы можете иметь синхронный код и не блокировать другие пользовательские события. Они по очереди ходят, но все еще работают в одном потоке. Таким образом, у Meteor есть синхронный код на стороне сервера, который может подождать, но другой пользователь не будет заблокирован этим и может делать что-то, потому что их код работает в другом волокне.

У Криса Мазера есть несколько хороших статей об этом на http://eventedmind.com

Что делает Meteor.wrapAsync?

Meteor.wrapAsync принимает метод, который вы даете ему в качестве первого параметра и запускает его в текущем волокне.

Он также прикрепляет обратный вызов к нему (он предполагает, что метод принимает последний параметр, который имеет обратный вызов, где первый параметр является ошибкой, а второй - результатом function(err,result).

Обратный вызов связан с Meteor.bindEnvironment и блокирует текущее волокно, пока не будет вызван обратный вызов. Как только обратный вызов срабатывает, он возвращает result или выбрасывает err.

Так что это очень удобно для преобразования асинхронного кода в синхронный код, так как вы можете использовать результат метода на следующей строке вместо того, чтобы использовать обратный вызов и встраивать более глубокие функции. Он также заботится о bindEnvironment для вас, поэтому вам не нужно беспокоиться о потере объема вашего волокна.

Обновление Meteor._wrapAsync теперь Meteor.wrapAsync и задокументировано.