Skip to content

Commit 9d2f2fc

Browse files
killagupopomore
authored andcommitted
feat: impl boot methods (#171)
1. Add `Lifecycle` class, control EggCore lifecycle. 2. After config, extend, app.js, angent.js loaded, loader should call `initBoots`. 3. After all files loaded, loader should call `triggerDidLoad`. Refs: eggjs/egg#2520
1 parent ae38fa4 commit 9d2f2fc

31 files changed

Lines changed: 1150 additions & 216 deletions

File tree

lib/egg.js

Lines changed: 58 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,12 @@ const BaseContextClass = require('./utils/base_context_class');
1111
const utils = require('./utils');
1212
const Router = require('./utils/router');
1313
const Timing = require('./utils/timing');
14+
const Lifecycle = require('./lifecycle');
1415

1516
const DEPRECATE = Symbol('EggCore#deprecate');
16-
const CLOSESET = Symbol('EggCore#closeSet');
17-
const ISCLOSE = Symbol('EggCore#isClose');
18-
const CLOSE_PROMISE = Symbol('EggCore#closePromise');
1917
const ROUTER = Symbol('EggCore#router');
2018
const EGG_LOADER = Symbol.for('egg#loader');
21-
const INIT_READY = Symbol('EggCore#initReady');
22-
19+
const CLOSE_PROMISE = Symbol('EggCore#closePromise');
2320

2421
class EggCore extends KoaApplication {
2522

@@ -44,16 +41,9 @@ class EggCore extends KoaApplication {
4441

4542
this.timing = new Timing();
4643

47-
// register a close set
48-
this[CLOSESET] = new Set();
4944
// cache deprecate object by file
5045
this[DEPRECATE] = new Map();
5146

52-
this[INIT_READY]();
53-
54-
this.timing.start('Application Start');
55-
this.ready(() => this.timing.end('Application Start'));
56-
5747
/**
5848
* @member {Object} EggCore#options
5949
* @private
@@ -108,6 +98,15 @@ class EggCore extends KoaApplication {
10898
*/
10999
this.Service = Service;
110100

101+
this.lifecycle = new Lifecycle({
102+
baseDir: options.baseDir,
103+
app: this,
104+
logger: this.console,
105+
});
106+
this.lifecycle.on('error', err => this.emit('error', err));
107+
this.lifecycle.on('ready_timeout', id => this.emit('ready_timeout', id));
108+
this.lifecycle.on('ready_stat', data => this.emit('ready_stat', data));
109+
111110
/**
112111
* The loader instance, the default class is {@link EggLoader}.
113112
* If you want define
@@ -207,111 +206,71 @@ class EggCore extends KoaApplication {
207206
* @param {Function|GeneratorFunction|AsyncFunction} scope function will execute before app start
208207
*/
209208
beforeStart(scope) {
210-
if (!is.function(scope)) {
211-
throw new Error('beforeStart only support function');
212-
}
213-
214-
// get filename from stack
215-
const name = utils.getCalleeFromStack(true);
216-
const timingkey = 'Before Start in ' + utils.getResolvedFilename(name, this.options.baseDir);
217-
218-
this.timing.start(timingkey);
219-
220-
const done = this.readyCallback(name);
209+
this.lifecycle.registerBeforeStart(scope);
210+
}
221211

222-
// ensure scope executes after load completed
223-
process.nextTick(() => {
224-
utils.callFn(scope).then(() => {
225-
done();
226-
this.timing.end(timingkey);
227-
}, err => {
228-
done(err);
229-
this.timing.end(timingkey);
230-
});
231-
});
212+
/**
213+
* register an callback function that will be invoked when application is ready.
214+
* @see https://github.com/node-modules/ready
215+
* @since 1.0.0
216+
* @param {boolean|Error|Function} flagOrFunction -
217+
* @return {Promise|null} return promise when argument is undefined
218+
* @example
219+
* const app = new Application(...);
220+
* app.ready(err => {
221+
* if (err) throw err;
222+
* console.log('done');
223+
* });
224+
*/
225+
ready(flagOrFunction) {
226+
return this.lifecycle.ready(flagOrFunction);
232227
}
233228

234229
/**
235-
* Close all, it will close
236-
* - callbacks registered by beforeClose
237-
* - emit `close` event
238-
* - remove add listeners
230+
* If a client starts asynchronously, you can register `readyCallback`,
231+
* then the application will wait for the callback to ready
239232
*
240-
* If error is thrown when it's closing, the promise will reject.
241-
* It will also reject after following call.
242-
* @return {Promise} promise
233+
* It will log when the callback is not invoked after 10s
234+
*
235+
* Recommend to use {@link EggCore#beforeStart}
243236
* @since 1.0.0
237+
*
238+
* @param {String} name - readyCallback task name
239+
* @param {object} opts -
240+
* - {Number} [timeout=10000] - emit `ready_timeout` when it doesn't finish but reach the timeout
241+
* - {Boolean} [isWeakDep=false] - whether it's a weak dependency
242+
* @return {Function} - a callback
243+
* @example
244+
* const done = app.readyCallback('mysql');
245+
* mysql.ready(done);
244246
*/
245-
close() {
246-
if (this[CLOSE_PROMISE]) return this[CLOSE_PROMISE];
247-
248-
const closeFunction = async () => {
249-
// close in reverse order: first created, last closed
250-
const closeFns = Array.from(this[CLOSESET]);
251-
for (const fn of closeFns.reverse()) {
252-
await utils.callFn(fn);
253-
this[CLOSESET].delete(fn);
254-
}
255-
// Be called after other close callbacks
256-
this.emit('close');
257-
this.removeAllListeners();
258-
this[ISCLOSE] = true;
259-
};
260-
this[CLOSE_PROMISE] = closeFunction();
261-
return this[CLOSE_PROMISE];
247+
readyCallback(name, opts) {
248+
return this.lifecycle.legacyReadyCallback(name, opts);
262249
}
263250

264251
/**
265252
* Register a function that will be called when app close
266253
* @param {Function} fn - the function that can be generator function or async function
267254
*/
268255
beforeClose(fn) {
269-
assert(is.function(fn), 'argument should be function');
270-
this[CLOSESET].add(fn);
256+
this.lifecycle.registerBeforeClose(fn);
271257
}
272258

273259
/**
274-
* @member {Function}
275-
* @private
260+
* Close all, it will close
261+
* - callbacks registered by beforeClose
262+
* - emit `close` event
263+
* - remove add listeners
264+
*
265+
* If error is thrown when it's closing, the promise will reject.
266+
* It will also reject after following call.
267+
* @return {Promise} promise
268+
* @since 1.0.0
276269
*/
277-
[INIT_READY]() {
278-
/**
279-
* register an callback function that will be invoked when application is ready.
280-
* @method {Function} EggCore#ready
281-
* @see https://github.com/node-modules/ready
282-
* @since 1.0.0
283-
* @example
284-
* const app = new Application(...);
285-
* app.ready(err => {
286-
* if (err) throw err;
287-
* console.log('done');
288-
* });
289-
*/
290-
291-
// get app timeout from env or use default timeout 10 second
292-
const eggReadyTimeoutEnv = Number.parseInt(process.env.EGG_READY_TIMEOUT_ENV || 10000);
293-
assert(Number.isInteger(eggReadyTimeoutEnv), `process.env.EGG_READY_TIMEOUT_ENV ${process.env.EGG_READY_TIMEOUT_ENV} should be able to parseInt.`);
294-
295-
/**
296-
* If a client starts asynchronously, you can register `readyCallback`,
297-
* then the application will wait for the callback to ready
298-
*
299-
* It will log when the callback is not invoked after 10s
300-
*
301-
* Recommend to use {@link EggCore#beforeStart}
302-
* @method {Function} EggCore#readyCallback
303-
* @since 1.0.0
304-
* @example
305-
* const done = app.readyCallback('mysql');
306-
* mysql.ready(done);
307-
*/
308-
require('ready-callback')({ timeout: eggReadyTimeoutEnv }).mixin(this);
309-
310-
this.on('ready_stat', data => {
311-
this.console.info('[egg:core:ready_stat] end ready task %s, remain %j', data.id, data.remain);
312-
}).on('ready_timeout', id => {
313-
this.console.warn('[egg:core:ready_timeout] %s seconds later %s was still unable to finish.', eggReadyTimeoutEnv / 1000, id);
314-
});
270+
async close() {
271+
if (this[CLOSE_PROMISE]) return this[CLOSE_PROMISE];
272+
this[CLOSE_PROMISE] = this.lifecycle.close();
273+
return this[CLOSE_PROMISE];
315274
}
316275

317276
/**

0 commit comments

Comments
 (0)