MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

O que está acontecendo com Meteor e Fibers/bindEnvironment()?


Você está usando o bindEnvironment de forma um pouco incorreta. Porque onde ele está sendo usado já está em uma fibra e o callback que sai do cliente Knox não está mais em uma fibra.

Existem dois casos de uso de bindEnvironment (que eu posso pensar, pode haver mais!):

  • Você tem uma variável global que precisa ser alterada, mas não deseja que ela afete as sessões de outros usuários

  • Você está gerenciando um retorno de chamada usando um módulo api/npm de terceiros (o que parece ser o caso)

Meteor.bindEnvironment cria uma nova Fibra e copia as variáveis ​​e o ambiente da Fibra atual para a nova Fibra. O ponto que você precisa disso é quando você usa o retorno de chamada do método do seu módulo nom.

Felizmente, existe uma alternativa que cuida do retorno de chamada esperando por você e vincula o retorno de chamada em uma fibra chamada Meteor.wrapAsync .

Então você poderia fazer isso:

Sua função de inicialização já tem uma fibra e nenhum retorno de chamada, então você não precisa de bindEnvironment aqui.
Meteor.startup(function () {
   if (Projects.find().count() === 0) {
     insertRecords();
   }
});

E sua função de registros de inserção (usando wrapAsync) para que você não precise de um retorno de chamada
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());
    }
  }      
});

Algumas coisas para manter em mente. Fibras não são como fios. Existe apenas um único thread no NodeJS.

As fibras são mais como eventos que podem ser executados ao mesmo tempo, mas sem bloquear uns aos outros se houver um cenário do tipo de espera (por exemplo, baixar um arquivo da Internet).

Assim você pode ter código síncrono e não bloquear os eventos do outro usuário. Eles se revezam para serem executados, mas ainda são executados em um único thread. Então é assim que o Meteor tem código síncrono no lado do servidor, que pode esperar por coisas, mas outros usuários não serão bloqueados por isso e podem fazer coisas porque seu código é executado em uma fibra diferente.

Chris Mather tem alguns bons artigos sobre isso em http://eventedmind.com

O que o Meteor.wrapAsync faz?


Meteor.wrapAsync recebe o método que você fornece como o primeiro parâmetro e o executa na fibra atual.

Ele também anexa um retorno de chamada a ele (ele assume que o método recebe um último parâmetro que tem um retorno de chamada onde o primeiro parâmetro é um erro e o segundo o resultado, como function(err,result) .

O retorno de chamada é vinculado a Meteor.bindEnvironment e bloqueia a fibra atual até que o retorno de chamada seja acionado. Assim que o callback é acionado, ele retorna o result ou lança o err .

Portanto, é muito útil para converter código assíncrono em código síncrono, pois você pode usar o resultado do método na próxima linha em vez de usar um retorno de chamada e aninhar funções mais profundas. Ele também cuida do bindEnvironment para você, para que você não precise se preocupar em perder o escopo de sua fibra.

Atualizar Meteor._wrapAsync agora é Meteor.wrapAsync e documentado.