Používáte bindEnvironment trochu nesprávně. Protože tam, kde se používá, je již vlákno a zpětné volání, které přichází z klienta Knox, již není ve vláknu.
Existují dva případy použití bindEnvironment (které mě napadají, mohlo by jich být více!):
-
Máte globální proměnnou, kterou je třeba změnit, ale nechcete, aby ovlivňovala relace ostatních uživatelů
-
Spravujete zpětné volání pomocí modulu API/npm třetí strany (což vypadá, že tomu tak je)
Meteor.bindEnvironment
vytvoří nové Fiber a zkopíruje aktuální Fiber proměnné a prostředí do nového Fiber. Bod, který potřebujete, je, když používáte zpětné volání metody vašeho modulu nom.
Naštěstí existuje alternativa, která se postará o zpětné volání, které na vás čeká, a spojí zpětné volání do vlákna s názvem Meteor.wrapAsync
.
Takže můžete udělat toto:
Vaše spouštěcí funkce již má vlákno a žádné zpětné volání, takže zde nepotřebujete bindEnvironment.
Meteor.startup(function () {
if (Projects.find().count() === 0) {
insertRecords();
}
});
A vaše funkce vkládání záznamů (pomocí wrapAsync), takže nepotřebujete zpětné volání
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());
}
}
});
Je třeba mít na paměti několik věcí. Vlákna nejsou jako nitě. V NodeJS je pouze jedno vlákno.
Vlákna jsou spíše události, které mohou běžet současně, ale bez vzájemného blokování, pokud existuje scénář typu čekání (např. stahování souboru z internetu).
Takže můžete mít synchronní kód a neblokovat události druhého uživatele. Střídají se v běhu, ale stále běží v jediném vláknu. Takže takhle má Meteor synchronní kód na straně serveru, který může čekat na věci, ale ostatní uživatelé tím nebudou blokováni a mohou dělat věci, protože jejich kód běží v jiném vláknu.
Chris Mather má na toto téma několik dobrých článků na http://eventedmind.com
Co dělá Meteor.wrapAsync?
Meteor.wrapAsync
vezme metodu, kterou zadáte jako první parametr, a spustí ji v aktuálním vláknu.
Také k němu připojí zpětné volání (předpokládá, že metoda vezme poslední parametr, který má zpětné volání, kde první parametr je chyba a druhý výsledek, jako je function(err,result)
.
Zpětné volání je spojeno s Meteor.bindEnvironment
a zablokuje aktuální vlákno, dokud se nespustí zpětné volání. Jakmile se spustí zpětné volání, vrátí result
nebo vyvolá err
.
Je to velmi užitečné pro převod asynchronního kódu na synchronní kód, protože můžete použít výsledek metody na dalším řádku namísto použití zpětného volání a vnoření hlubších funkcí. Také se za vás postará o bindEnvironment, takže se nemusíte bát, že ztratíte rozsah vašeho vlákna.
Aktualizovat Meteor._wrapAsync
je nyní Meteor.wrapAsync
a zdokumentováno.