¿Qué está pasando con Meteor and Fibers / bindEnvironment ()?

Estoy teniendo dificultades para usar Fibres / Meteor.bindEnvironment (). Intenté que el código se actualice y se inserte en una colección si la colección comienza vacía. Se supone que todo esto se ejecuta en el servidor en el inicio.

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(); } }); } 

La primera vez que escribí esto, obtuve los errores que necesitaba para envolver mis devoluciones de llamada en un bloque de Fibra (), luego, en una discusión sobre el IRC, alguien recomendó probar Meteor.bindEnvironment () en su lugar, ya que eso debería ser poner una Fibra. Eso no funcionó (la única salida que vi fue inserting... , lo que significa que bindEnvironment () no generó un error, pero tampoco ejecuta ningún código dentro del bloque). Entonces llegué a esto. Mi error ahora es: Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment. Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.

Soy nuevo en Node y no entiendo completamente el concepto de Fibras. Mi entendimiento es que son análogos a los subprocesos en C / C ++ / todos los lenguajes con subprocesos, pero no entiendo cuáles son las implicaciones que se extienden a mi código del lado del servidor / por qué mi código está generando un error al intentar insertarlo en Una colección. ¿Puede alguien explicarme esto?

Gracias.

Estás usando bindEnvironment un poco incorrecto. Porque donde se está utilizando ya está en una fibra y la callback que sale del cliente de Knox ya no está en una fibra.

Hay dos casos de uso de bindEnvironment (que puedo pensar, ¡podría haber más!):

  • Tiene una variable global que debe modificarse pero no desea que afecte a las sesiones de otros usuarios

  • Está administrando una callback utilizando un módulo de api / npm de terceros (que parece ser el caso)

Meteor.bindEnvironment crea una nueva fibra y copia las variables y el entorno actuales de la fibra a la nueva fibra. El punto que necesita esto es cuando usa la callback del método de su módulo nom.

Afortunadamente, existe una alternativa que se ocupa de la callback que lo espera y la vincula en una fibra llamada Meteor.wrapAsync .

Para que pudieras hacer esto:

Su función de inicio ya tiene una fibra y no hay callback, por lo que no necesita bindEnvironment aquí.

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

Y la función de inserción de registros (usando wrapAsync) para que no necesite una 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()); } } }); 

Un par de cosas a tener en cuenta. Las fibras no son como hilos. Hay un solo hilo en NodeJS.

Las fibras son más como eventos que pueden ejecutarse al mismo tiempo pero sin bloquearse entre sí si hay un escenario de tipo de espera (por ejemplo, descargar un archivo de Internet).

Por lo tanto, puede tener un código síncrono y no bloquear los eventos del otro usuario. Se turnan para correr, pero aún se ejecutan en un solo hilo. Así es como Meteor tiene un código síncrono en el servidor, que puede esperar por cosas, sin embargo, otros usuarios no serán bloqueados por esto y pueden hacer cosas porque su código se ejecuta en una fibra diferente.

Chris Mather tiene un par de buenos artículos sobre esto en http://eventedmind.com

¿Qué hace Meteor.wrapAsync?

Meteor.wrapAsync toma el método que le da como primer parámetro y lo ejecuta en la fibra actual.

También le adjunta una callback (asume que el método toma un último parámetro que tiene una callback donde el primer parámetro es un error y el segundo el resultado, como la function(err,result) .

La callback está vinculada a Meteor.bindEnvironment y bloquea la fibra actual hasta que se Meteor.bindEnvironment la callback. Tan pronto como se activa la callback, devuelve el result o lanza el err .

Por lo tanto, es muy útil para convertir código asíncrono en código síncrono, ya que puede usar el resultado del método en la siguiente línea en lugar de usar una callback y funciones de anidamiento más profundas. También se encarga del bindEnvironment para que usted no tenga que preocuparse por perder el scope de su fibra.

Actualizar Meteor._wrapAsync ahora es Meteor.wrapAsync y está documentado .