express.js(或类似)应用程序的异步初始化

Iva*_*hko 9 javascript asynchronous web-frameworks node.js express

考虑一个例子:我有以下express.js应用程序(请参阅下面的代码片段).我希望在整个应用程序生命周期中有一个与DB的持久连接,以及一个与我自己的服务的持久连接(需要异步调用才能启动).还有一些入口点,即不仅可以通过HTTP协议访问我的应用程序.当然,我想避免服务初始化代码重复,并且可能有几个这样的异步初始化服务.

/* app.js */
var app = require('express')();
// set views, use routes, etc.
var db = require('monk/mongoose/etc')(...); // happily, usually it's a sync operation
var myService = require('./myService');     // however, it's possible to have several such services
myService.init(function(err, result) {
    // only here an initialization process is finished!
});

module.exports.app = app;


/* http_server.js (www entry point) */
var app = require('app');
// create an HTTP server with this app and start listening


/* telnet_server.js (other entry point) */
var app = require('app');
// create a Telnet server with this app and start listening
Run Code Online (Sandbox Code Playgroud)

在上面的代码片段中,当http(或telnet或任何其他)服务器启动时,无法保证myService已经初始化.

所以,我必须以某种方式重新组织我的应用创建代码.现在我坚持下一个解决方案:

/* app.js */
var app = require('express')();
module.exports.app = app;
module.exports.init = function(callback) {
    var myService = require('./myService');
    myService.init(callback);     
}

/* entry_point.js */
var app = require('app');
app.init(function(err) {
    if (!err) {
        // create an HTTP/Telnet/etc server and start listening
    }
});
Run Code Online (Sandbox Code Playgroud)

所以,我的问题是:初始化服务需要异步调用启动的常用方法是什么?

小智 0

在这里创建了一个要点,其中包含我通常用于此任务的代码示例。(它使用 Q Promise 库,但可以轻松修改为使用任何其他 Promise 库)。

基本思想是将应用程序主干描述为一系列异步初始化步骤。每一步都会调用一个或多个异步函数并将结果绑定到一个名称;只有当前步骤的所有值都解析完毕后,启动过程才会进入下一个初始化步骤,并且后续步骤可以访问先前步骤解析的所有值。这样可以轻松描述应用程序内服务和组件的依赖顺序。

例如,主干可以定义如下:

var app = [
  { s1: startService1 },
  { s2: startService2, s3: startService3 },
  { s4: startService4 }
]
Run Code Online (Sandbox Code Playgroud)

(请注意,在每个步骤定义中,仅给出对每个函数的引用;该start()函数(在要点中显示)将以正确的顺序调用每个函数)。

每个startXxx变量都是一个函数,它接受一个参数,并返回一个延迟的 Promise,例如:

function startService4( app ) {
    var s1 = app.s1;
    var s2 = app.s2;
    var deferred = Q.defer();
    // ... start the service, do async stuff ...
    return deferred;
}
Run Code Online (Sandbox Code Playgroud)

该函数的app参数表示已配置的应用程序主干,并且先前初始化步骤的结果可作为其命名属性使用。

我已经在相当复杂的系统中使用了这种模式,并发现它是定义系统高级结构的一种简单、灵活且有效的方法。