Skip to content

Commit ec2b32d

Browse files
author
Robert Jackson
committed
Refactor destruction (Ember.Application and Ember.ApplicationInstance)
* Collapse `createEmberApp` into `buildApp` (since we are no longer separating the concepts of `Ember.Application` vs `Ember.ApplicationInstance`) * Stash the `Ember.Application` instance (along with the `Ember.ApplicationInstance` instance) after creation, for destruction later * Ensure destruction of both `Ember.Application` and `Ember.ApplicationInstance` occur when the `visit` has completed.
1 parent edeeb8f commit ec2b32d

3 files changed

Lines changed: 58 additions & 66 deletions

File tree

src/ember-app.js

Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -180,29 +180,6 @@ class EmberApp {
180180
};
181181
}
182182

183-
/**
184-
* @private
185-
*
186-
* Create the ember application in the sandbox.
187-
*
188-
*/
189-
createEmberApp(sandbox) {
190-
// Retrieve the application factory from within the sandbox
191-
let AppFactory = sandbox.run(function(ctx) {
192-
return ctx.require('~fastboot/app-factory');
193-
});
194-
195-
// If the application factory couldn't be found, throw an error
196-
if (!AppFactory || typeof AppFactory['default'] !== 'function') {
197-
throw new Error(
198-
'Failed to load Ember app from app.js, make sure it was built for FastBoot with the `ember fastboot:build` command.'
199-
);
200-
}
201-
202-
// Otherwise, return a new `Ember.Application` instance
203-
return AppFactory['default']();
204-
}
205-
206183
/**
207184
* Perform any cleanup that is needed
208185
*/
@@ -211,11 +188,11 @@ class EmberApp {
211188
/**
212189
* @private
213190
*
214-
* Creates a new `ApplicationInstance` from the sandboxed `Application`.
191+
* Creates a new `Application`
215192
*
216-
* @returns {Promise<Ember.ApplicationInstance>} instance
193+
* @returns {Ember.Application} instance
217194
*/
218-
async buildAppInstance() {
195+
async buildApp() {
219196
let sandbox = this.buildSandbox();
220197

221198
debug('adding files to sandbox');
@@ -227,10 +204,26 @@ class EmberApp {
227204

228205
debug('files evaluated');
229206

230-
let app = await this.createEmberApp(sandbox).boot();
207+
// Retrieve the application factory from within the sandbox
208+
let AppFactory = sandbox.run(function(ctx) {
209+
return ctx.require('~fastboot/app-factory');
210+
});
231211

232-
debug('building instance');
233-
return app.buildInstance();
212+
// If the application factory couldn't be found, throw an error
213+
if (!AppFactory || typeof AppFactory['default'] !== 'function') {
214+
throw new Error(
215+
'Failed to load Ember app from app.js, make sure it was built for FastBoot with the `ember fastboot:build` command.'
216+
);
217+
}
218+
219+
debug('creating application');
220+
221+
// Otherwise, return a new `Ember.Application` instance
222+
let app = AppFactory['default']();
223+
224+
await app.boot();
225+
226+
return app;
234227
}
235228

236229
/**
@@ -252,9 +245,12 @@ class EmberApp {
252245
* @return {Promise<instance>} instance
253246
*/
254247
async visitRoute(path, fastbootInfo, bootOptions, result) {
255-
let instance = await this.buildAppInstance();
248+
let app = await this.buildApp();
249+
result.applicationInstance = app;
250+
251+
let instance = await app.buildInstance();
252+
result.applicationInstanceInstance = instance;
256253

257-
result.instance = instance;
258254
registerFastBootInfo(fastbootInfo, instance);
259255

260256
await instance.boot(bootOptions);
@@ -307,7 +303,7 @@ class EmberApp {
307303
// start a timer to destroy the appInstance forcefully in the given ms.
308304
// This is a failure mechanism so that node process doesn't get wedged if the `visit` never completes.
309305
destroyAppInstanceTimer = setTimeout(function() {
310-
if (result._destroyAppInstance()) {
306+
if (result._destroy()) {
311307
result.error = new Error(
312308
'App instance was forcefully destroyed in ' + destroyAppInstanceInMs + 'ms'
313309
);
@@ -328,11 +324,12 @@ class EmberApp {
328324
// eslint-disable-next-line require-atomic-updates
329325
result.error = error;
330326
} finally {
331-
if (result._destroyAppInstance()) {
332-
if (destroyAppInstanceTimer) {
333-
clearTimeout(destroyAppInstanceTimer);
334-
}
335-
}
327+
// ensure we invoke `Ember.Application.destroy()` and
328+
// `Ember.ApplicationInstance.destroy()`, but use `result._destroy()` so
329+
// that the `result` object's internal `this.isDestroyed` flag is correct
330+
result._destroy();
331+
332+
clearTimeout(destroyAppInstanceTimer);
336333
}
337334

338335
return result;

src/result.js

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ const HTML_HEAD_REGEX = /^([\s\S]*<\/head>)([\s\S]*)/;
1313
*/
1414
class Result {
1515
constructor(doc, html, fastbootInfo) {
16-
this._instanceDestroyed = false;
16+
this.isDestroyed = false;
1717

1818
this._doc = doc;
1919
this._html = html;
2020
this._fastbootInfo = fastbootInfo;
21-
this.instance = undefined;
21+
this.applicationInstance = undefined;
22+
this.applicationInstanceInstance = undefined;
2223
}
2324

2425
/**
@@ -113,10 +114,10 @@ class Result {
113114
/**
114115
* @private
115116
*
116-
* Called once the Result has finished being constructed and the application
117-
* instance has finished rendering. Once `finalize()` is called, state is
118-
* gathered from the completed application instance and statically copied
119-
* to this Result instance.
117+
* Called once the Result has finished being constructed and the
118+
* ApplicationInstance instance has finished rendering. Once `finalize()` is
119+
* called, state is gathered from the completed ApplicationInstance instance
120+
* and statically copied to this Result instance.
120121
*/
121122
_finalize() {
122123
if (this.finalized) {
@@ -125,9 +126,9 @@ class Result {
125126

126127
// Grab some metadata from the sandboxed application instance
127128
// and copy it to this Result object.
128-
let { instance } = this;
129-
if (instance) {
130-
this._finalizeMetadata(instance);
129+
let { applicationInstanceInstance } = this;
130+
if (applicationInstanceInstance) {
131+
this._finalizeMetadata(applicationInstanceInstance);
131132
}
132133

133134
this._finalizeHTML();
@@ -136,9 +137,9 @@ class Result {
136137
return this;
137138
}
138139

139-
_finalizeMetadata(instance) {
140-
if (instance._booted) {
141-
this.url = instance.getURL();
140+
_finalizeMetadata(applicationInstanceInstance) {
141+
if (applicationInstanceInstance._booted) {
142+
this.url = applicationInstanceInstance.getURL();
142143
}
143144

144145
let { response } = this._fastbootInfo;
@@ -149,15 +150,20 @@ class Result {
149150
}
150151
}
151152

152-
_destroyAppInstance() {
153-
if (this.instance && !this._instanceDestroyed) {
154-
this._instanceDestroyed = true;
155-
this.instance.destroy();
156-
153+
_destroy() {
154+
if (this.isDestroyed) {
157155
return true;
158156
}
159157

160-
return false;
158+
this.isDestroyed = true;
159+
160+
if (this.applicationInstanceInstance !== undefined) {
161+
this.applicationInstanceInstance.destroy();
162+
}
163+
164+
if (this.applicationInstance !== undefined) {
165+
this.applicationInstance.destroy();
166+
}
161167
}
162168

163169
_finalizeHTML() {

test/fastboot-test.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -387,17 +387,6 @@ describe('FastBoot', function() {
387387
return fastboot.visit('/').catch(e => expect(e).to.be.an('error'));
388388
});
389389

390-
it("matches app's fastboot-info and result's fastboot-info", function() {
391-
var fastboot = new FastBoot({
392-
distPath: fixture('basic-app'),
393-
});
394-
395-
return fastboot.visit('/').then(r => {
396-
let lookupFastboot = r.instance.lookup('info:-fastboot');
397-
expect(r._fastbootInfo).to.deep.equal(lookupFastboot);
398-
});
399-
});
400-
401390
it('can read multiple configs', function() {
402391
var fastboot = new FastBoot({
403392
distPath: fixture('app-with-multiple-config'),

0 commit comments

Comments
 (0)