javascript - Node.js - modulcaching, delvis färdiga objekt och cykliska beroenden?


I node.js-dokumentationen om modulcaching görs följande uttalande:

Flera anrop till require('foo') kanske inte orsakar att modulens kod körs flera gånger. Detta är en viktig funktion. Med den kan "delvis färdiga" objekt returneras, vilket möjliggör att transitiva beroenden kan laddas även när de skulle orsaka cykler.

Jag är lite förvirrad över den sista meningen. Vad är ett "delvis färdigt" objekt? Hur relaterar detta till att tillåta (eller undvika) cykliska beroenden?

Svar #1

Om du require ett paket från en fil, och det gör att en fil i det paketet require filen som orsakade den initiala require, då har du ett cykliskt beroende. Som standard skulle det bara gå i cirklar. För att förhindra detta kan man behålla en markör där den ursprungliga require startade, så att nästa gång den filen blir require'd startar den från den punkten istället för från början. Det är inte felfritt, men när det gäller att ladda ett paket är man generellt sett bara intresserad av exporterna, och det fungerar bra i det fallet.

Jag skickade en diff för node-browserify för ett tag sedan för en primitiv metod för "delvis klara" exports. I princip, varje gång något blir require

Svar #2

Det finns ett trevligt och tydligt exempel i node.js-dokumentationen. Det belyser mer om det "delvis färdiga" objektet och cykliska beroenden.

När det finns cirkulära require()-anrop kanske ett modul inte har avslutat sin körning när det returneras.

Tänk på denna situation:

a.js:

console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');

b.js:

console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');

main.js:

console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);

När main.js laddar a.js, laddar a.js i sin tur b.js. Vid det tillfället försöker b.js ladda a.js. För att förhindra en oändlig loop returneras en ofullständig kopia av a.js exports-objektet till b.js-modulen. b.js slutför sedan sin laddning, och dess exports-objekt tillhandahålls till a.js-modulen.

När main.js har laddat båda modulerna, är de båda klara. Utdata från detta program skulle därför vara:

node main.js

main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true