Node.js und asynchrone Module


Hier die Ausgangslage:

  • Ein Module, welches einen Ordner auf Dropbox mit einem lokalen Ordner synchronisiert
  • Die App ist multi Mandantenfähig, sprich, es können Ordner von mehreren Dropboxen synchronisiert werden

Und hier ist das geniale von Node.js, bzw. asynchroner Programmierung: Bei einem solchen Vorgang verbringt die App wohl die meiste Zeit mit warten: Dropbox Authentifizierung und Download der Daten ist nicht sehr rechenintensiv dafür sehr Zeitintensiv, wobei die meiste Zeit mit warten verbracht wird. Mit Node.js lässt sich das super einfach parallel abarbeiten, aber Vorsicht.

Mein vereinfachtes Problem sieht wie folgt aus:

// test.js
var name = "raphael";
exports.updateName = function(namein) {
    name = namein;
    setTimeout(function(){console.log("my name is: " + name)}, 1000);
}

// app.js
names = ["Annika", "linea", "nele"];
names.forEach(function(name){
    var namer = require('test');
    namer.updateName(name);
});

Was ist wohl das Resultat davon?

nele
nele
nele

?????? Ich bin eigentlich immer davon ausgegangen, dass ein Modul in einem eigenen Scope abläuft. Dann habe ich noch folgendes ausprobiert:

names = ["Annika", "linea", "nele"];
var namers = [];
var i = 0;
names.forEach(function(name){
    namers[i] = require('test');
    namers[i].updateName(name);
    i++;
});

Eigentlich würde man annehmen, dass hier 3x eine neue "Instanz" von test angelegt wird, doch weit gefehlt: auch hier ist das Resultat:

nele
nele
nele

Und so funktioniert es:

exports.updateName = function(namein) {
    var name = namein;
    setTimeout(function(){console.log("my name is: " + name)}, 1000);
}

Das funktioniert jetzt natürlich wunderbar, da es sich lediglich um eine Funktion handelt, aber ich habe noch ein paar andere, welche auf diese name Variable zugreifen müssen. Ja ich könnte ein richtiges Object draus machen und dann this verwenden, doch im asynchronen Context funktioniert das dann leider nicht ganz fehlerfrei, da sich jeweils der Scope von this ändert.

Update 15.08.2013: Und jetzt auch noch die (offizielle Erklärung)[http://nodejs.org/api/modules.html#modules_caching] von der Node.js Docs Seite:

Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo') will get exactly the same object returned, if it would resolve to the same file.

Multiple calls to require('foo') may not cause the module code to be executed multiple times. This is an important feature. With it, "partially done" objects can be returned, thus allowing transitive dependencies to be loaded even when they would cause cycles.

If you want to have a module execute code multiple times, then export a function, and call that function.