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

Posted

技术标签:

【中文标题】express.js(或类似)应用程序的异步初始化【英文标题】:Asynchronous initialization of express.js (or similar) apps 【发布时间】:2016-04-12 03:41:59 【问题描述】:

考虑一个例子:我有以下express.js 应用程序(参见下面的代码 sn-p)。我想在整个应用程序生命周期内拥有一个与数据库的持久连接,以及一个与我自己的服务(需要异步调用才能启动)的持久连接。并且有几个入口点,即一个人不仅可以通过 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

在上面的代码 sn-p 中,当 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
    
);

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

【问题讨论】:

这与序列化任何异步操作集的方式确实没有什么不同。如果您希望它们按特定顺序完成,则链接承诺或在第一个异步操作的完成回调中执行第二个操作。如果你有 N 个独立的异步操作应该在你做其他事情之前完成,那么使用 Promise 和 Promise.all()。启动服务器与多个异步操作的任何其他协调没有什么不同。 【参考方案1】:

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

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

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

var app = [
   s1: startService1 ,
   s2: startService2, s3: startService3 ,
   s4: startService4 
]

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

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

function startService4( app ) 
    var s1 = app.s1;
    var s2 = app.s2;
    var deferred = Q.defer();
    // ... start the service, do async stuff ...
    return deferred;

函数的 app 参数表示已配置的应用主干,之前初始化步骤的结果可用作其命名属性。

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

【讨论】:

【参考方案2】:

我建议您承诺您的服务的初始化功能,然后以下列方式使用它们:

const app = require('express')();
const util = require('util');
const myService = require('./myService');
const myServiceInit = util.promisify(myService.init);
Promise.all([myServiceInit]).then(() => 
  // delayed listening of your app
  app.listen(2000);
).catch(err => 
 // handle error here
);

我使用 Promise.all 来为您添加多个内部服务的初始化。

承诺初始化函数的先决条件是它应该使用错误优先回调机制。你可以在这里阅读更多关于它的信息Node Official Doc

希望这对您的事业有所帮助。

【讨论】:

如果它是你的服务,你不需要util.promisify,你应该重写init函数来已经返回一个promise本身。

以上是关于express.js(或类似)应用程序的异步初始化的主要内容,如果未能解决你的问题,请参考以下文章

Express.js 应用程序无服务器,使用 Lambda 或函数 - 好主意吗?

从 node.js/express.js 中的多个异步源构建对象

Node.js / Sequelize.js / Express.js - 如何插入多对多关联? (同步/异步?)

Express JS 在请求上使用异步函数

将 Express.js 3 与数据库模块一起使用,在哪里初始化数据库客户端?

node.js + express.js:使用 mongodb/mongoose 处理会话