Skip to content

Commit

Permalink
Fixes #886 - Write component initialization code when async out and a…
Browse files Browse the repository at this point in the history
…ll of its nested async outs finish

Store async tracking information independently on each out. Completion now trickles up the tree of outs
  • Loading branch information
patrick-steele-idem committed Oct 11, 2017
1 parent be1e2e0 commit bff787d
Show file tree
Hide file tree
Showing 37 changed files with 683 additions and 519 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
"minimatch": "^3.0.2",
"object-assign": "^4.1.0",
"property-handlers": "^1.0.0",
"raptor-async": "^1.1.2",
"raptor-json": "^1.0.1",
"raptor-polyfill": "^1.0.0",
"raptor-promises": "^1.0.1",
Expand Down
193 changes: 99 additions & 94 deletions src/runtime/html/AsyncStream.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,20 @@ function State(root, stream, writer, events) {
this.writer = writer;
this.events = events;

this.remaining = 0;
this.lastCount = 0;
this.last = undefined; // Array
this.ended = false;
this.finished = false;
this.ids = 0;
}

function AsyncStream(global, writer, state, shouldBuffer) {
function AsyncStream(global, writer, parentOut, shouldBuffer) {

if (parentOut === null) {
throw new Error('illegal state');
}
var finalGlobal = this.attributes = global || {};
var originalStream;
var state;

if (state) {
if (parentOut) {
state = parentOut._state;
originalStream = state.stream;
} else {
var events = finalGlobal.events /* deprecated */ = writer && writer.on ? writer : new EventEmitter();
Expand All @@ -48,6 +49,12 @@ function AsyncStream(global, writer, state, shouldBuffer) {
this.stream = originalStream;
this._state = state;

this._ended = false;
this._remaining = 1;
this._lastCount = 0;
this._last = undefined; // Array
this._parentOut = parentOut;

this.data = {};
this.writer = writer;
writer.stream = this;
Expand Down Expand Up @@ -137,7 +144,7 @@ var proto = AsyncStream.prototype = {
┗━━━━━┛ prevWriter → currentWriter → nextWriter */

var newWriter = new StringWriter();
var newStream = new AsyncStream(this.global, currentWriter, state);
var newStream = new AsyncStream(this.global, currentWriter, this);

this.writer = newWriter;
newWriter.stream = this;
Expand All @@ -152,7 +159,7 @@ var proto = AsyncStream.prototype = {
var timeout;
var name;

state.remaining++;
this._remaining++;

if (options != null) {
if (typeof options === 'number') {
Expand All @@ -167,7 +174,7 @@ var proto = AsyncStream.prototype = {
timeout = 0;
}

state.lastCount++;
this._lastCount++;
}

name = options.name;
Expand All @@ -194,11 +201,31 @@ var proto = AsyncStream.prototype = {
parentOut: this
});

return newStream;
return newStream;
},

_doFinish: function() {
var state = this._state;

state.finished = true;

if (state.writer.end) {
state.writer.end();
} else {
state.events.emit('finish', this.___getResult());
}
},

end: function(data) {
if (data) {
if (this._ended === true) {
return;
}

this._ended = true;

var remaining = --this._remaining;

if (data != null) {
this.write(data);
}

Expand All @@ -213,88 +240,55 @@ var proto = AsyncStream.prototype = {
currentWriter.stream = null;

// Flush the contents of nextWriter to the currentWriter
this.flushNext(currentWriter);
this._flushNext(currentWriter);

/* ┏━━━━━┓ this ╵ nextStream
┃ ┃ ↓ ╵ ↓↑
┃ NOW ┃ voidWriter ╵ currentWriter → futureWriter
┃ ┃ ──────────────┴────────────────────────────────
┗━━━━━┛ Flushed & garbage collected: nextWriter */

var parentOut = this._parentOut;

var state = this._state;

if (state.finished) {
return;
}

var remaining;

if (this === state.root) {
remaining = state.remaining;
state.ended = true;
if (parentOut === undefined) {
if (remaining === 0) {
this._doFinish();
} else if (remaining - this._lastCount === 0) {
this._emitLast();
}
} else {
var timeoutId = this._timeoutId;

if (timeoutId) {
clearTimeout(timeoutId);
}

remaining = --state.remaining;
}

if (state.ended) {
if (!state.lastFired && (state.remaining - state.lastCount === 0)) {
state.lastFired = true;
state.lastCount = 0;
state.events.emit('last');
}

if (remaining === 0) {
state.finished = true;

if (state.writer.end) {
state.writer.end();
} else {
state.events.emit('finish', this.___getResult());
}
parentOut._handleChildDone();
} else if (remaining - this._lastCount === 0) {
this._emitLast();
}
}

return this;
},

// flushNextOld: function(currentWriter) {
// if (currentWriter === this._state.writer) {
// var nextStream;
// var nextWriter = currentWriter.next;
//
// // flush until there is no nextWriter
// // or the nextWriter is still attached
// // to a branch.
// while(nextWriter) {
// currentWriter.write(nextWriter.toString());
// nextStream = nextWriter.stream;
//
// if(nextStream) break;
// else nextWriter = nextWriter.next;
// }
//
// // Orphan the nextWriter and everything that
// // came before it. They have been flushed.
// currentWriter.next = nextWriter && nextWriter.next;
//
// // If there is a nextStream,
// // set its writer to currentWriter
// // (which is the state.writer)
// if(nextStream) {
// nextStream.writer = currentWriter;
// currentWriter.stream = nextStream;
// }
// }
// },

flushNext: function(currentWriter) {
_handleChildDone: function() {
var remaining = --this._remaining;

if (remaining === 0) {
var parentOut = this._parentOut;
if (parentOut === undefined) {
this._doFinish();
} else {
parentOut._handleChildDone();
}
} else if (remaining - this._lastCount === 0) {
this._emitLast();
}
},

_flushNext: function(currentWriter) {
// It is possible that currentWriter is the
// last writer in the chain, so let's make
// sure there is a nextWriter to flush.
Expand Down Expand Up @@ -324,50 +318,61 @@ var proto = AsyncStream.prototype = {
on: function(event, callback) {
var state = this._state;

if (event === 'finish' && state.finished) {
if (event === 'finish' && state.finished === true) {
callback(this.___getResult());
return this;
} else if (event === 'last') {
this.onLast(callback);
} else {
state.events.on(event, callback);
}

state.events.on(event, callback);
return this;
},

once: function(event, callback) {
var state = this._state;

if (event === 'finish' && state.finished) {
if (event === 'finish' && state.finished === true) {
callback(this.___getResult());
return this;
} else if (event === 'last') {
this.onLast(callback);
} else {
state.events.once(event, callback);
}

state.events.once(event, callback);
return this;
},

onLast: function(callback) {
var state = this._state;
var lastArray = this._last;

var lastArray = state.last;
if (lastArray === undefined) {
this._last = [callback];
} else {
lastArray.push(callback);
}

if (!lastArray) {
lastArray = state.last = [];
var i = 0;
var next = function next() {
if (i === lastArray.length) {
return;
}
var _next = lastArray[i++];
_next(next);
};
return this;
},

_emitLast: function() {
var lastArray = this._last;

var i = 0;

this.once('last', function() {
function next() {
if (i === lastArray.length) {
return;
}
var lastCallback = lastArray[i++];
lastCallback(next);

if (lastCallback.length === 0) {
next();
});
}
}

lastArray.push(callback);
return this;
next();
},

emit: function(type, arg) {
Expand Down
6 changes: 3 additions & 3 deletions src/runtime/html/Template.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Readable extends stream.Readable {
var data = this._d;
var globalData = data && data.$global;
var shouldBuffer = this._shouldBuffer;
var out = new AsyncStream(globalData, this, null, shouldBuffer);
var out = new AsyncStream(globalData, this, undefined, shouldBuffer);
template.render(data, out);
out.end();
}
Expand All @@ -46,8 +46,8 @@ function Template(path, renderFunc, options) {
this.meta = undefined;
}

function createOut(globalData, parent, state, buffer) {
return new AsyncStream(globalData, parent, state, buffer);
function createOut(globalData, writer, parentOut, buffer) {
return new AsyncStream(globalData, writer, parentOut, buffer);
}

Template.prototype = {
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/renderable.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ module.exports = function(target, renderer) {
finalOut = createOut(
globalData, // global
out, // writer(AsyncStream) or parentNode(AsyncVDOMBuilder)
null, // state
undefined, // parentOut
shouldBuffer // ignored by AsyncVDOMBuilder
);
}
Expand Down
Loading

0 comments on commit bff787d

Please sign in to comment.