学习使用Nodejs的eventproxy解决深层回调函数时遇到的问题

学习使用Nodejs的eventproxy解决深层回调函数时遇到的问题

众所周知eventproxy是一个解决深层回调函数的一个模块。我用的时候出现了这个问题:Error: db object already connecting, open cannot be called multiple times
参考他的readMe的例子

var ep = EventProxy.create("template", "data", "l10n", function (template, data, l10n) {
  _.template(template, data, l10n);
});

$.get("template", function (template) {
  // something
  ep.emit("template", template);
});
$.get("data", function (data) {
  // something
  ep.emit("data", data);
});
$.get("l10n", function (l10n) {
  // something
  ep.emit("l10n", l10n);
});

似乎是因为我的get方法里面用了mongodb.open()这样的方法造成的不能多次打开数据库问题

我的代码:

var render = function (user, blogs) {
        res.render("user", {title:user.name, blogs:blogs});

    }
    EventProxy.create("user", "blogs", render);
    User.get(req.params.user, function (err, user) {
        if (!user) {
            req.flash("error", "用户不存在");
            return res.redirect("/");
        }
        proxy.emit("user", user);
    });
    Blog.get(req.params.user, function (err, blogs) {
        if (err) {
            req.flash("error", err);
            return res.redirect("/");
        }
        proxy.emit("blogs", blogs);
    });

Blog.get 代码:

Blog.get = function get(username, callback) {
        mongodb.open(function (err, db) {
            if (err) {
                return callback(err);
            }
            // 讀取 blogs 集合
            db.collection('blogs', function (err, collection) {
                //处理
            });
        });
    };

User.get代码

User.get = function (username, callback) {
    db.open(function (err, db) {
        if (err) {
            return callback(err);
        }
        db.collection("users", function (err, collection) {
            //处理
        });
    });
}

这个有什么好的解决办法吗?好像因为读取数据库造成的深层回调事件好像还蛮多的


7 回复

学习使用Node.js的EventProxy解决深层回调函数时遇到的问题

背景介绍

EventProxy 是一个用于解决深层嵌套回调问题的 Node.js 模块。它可以帮助你更好地管理异步操作,避免回调地狱(callback hell)。然而,在实际使用中可能会遇到一些问题,比如多次尝试打开同一个数据库连接。

问题描述

在你的例子中,你遇到了 Error: db object already connecting, open cannot be called multiple times 错误。这表明你在尝试多次打开同一个 MongoDB 数据库连接,而 MongoDB 不允许这种情况发生。

示例代码及问题分析

首先,我们来看一下你的代码:

// 渲染函数
var render = function (user, blogs) {
    res.render("user", { title: user.name, blogs: blogs });
};

// 创建 EventProxy 实例
EventProxy.create("user", "blogs", render);

// 获取用户信息
User.get(req.params.user, function (err, user) {
    if (!user) {
        req.flash("error", "用户不存在");
        return res.redirect("/");
    }
    proxy.emit("user", user);
});

// 获取博客信息
Blog.get(req.params.user, function (err, blogs) {
    if (err) {
        req.flash("error", err);
        return res.redirect("/");
    }
    proxy.emit("blogs", blogs);
});

接下来,我们看看 Blog.getUser.get 的实现:

// Blog.get 方法
Blog.get = function get(username, callback) {
    mongodb.open(function (err, db) {
        if (err) {
            return callback(err);
        }
        // 讀取 blogs 集合
        db.collection('blogs', function (err, collection) {
            // 处理
        });
    });
};

// User.get 方法
User.get = function (username, callback) {
    db.open(function (err, db) {
        if (err) {
            return callback(err);
        }
        db.collection("users", function (err, collection) {
            // 处理
        });
    });
};

解决方案

为了避免多次打开同一个数据库连接,可以将数据库连接的初始化放在一个全局变量中,或者使用一个单例模式来确保只打开一次连接。这里提供一种解决方案:

// 初始化数据库连接
let db;

function initDb(callback) {
    if (db) {
        return callback(null, db);
    }
    mongodb.open(function (err, _db) {
        if (err) {
            return callback(err);
        }
        db = _db;
        callback(null, db);
    });
}

// 更新 User.get 方法
User.get = function (username, callback) {
    initDb(function (err, db) {
        if (err) {
            return callback(err);
        }
        db.collection("users", function (err, collection) {
            // 处理
        });
    });
};

// 更新 Blog.get 方法
Blog.get = function get(username, callback) {
    initDb(function (err, db) {
        if (err) {
            return callback(err);
        }
        db.collection('blogs', function (err, collection) {
            // 处理
        });
    });
};

通过这种方式,你可以确保数据库连接只被打开一次,并且可以在多个地方复用这个连接。这样就能避免多次打开同一个数据库连接导致的问题。


db应该是个单实例。实例内做连接池

db.open(function(err, db){
    var render = function (user, blogs) {
        db.close();
        res.render("user", {title:user.name, blogs:blogs});

    }
    EventProxy.create("user", "blogs", render);
    User.get(req.params.user, function (err, user) {
        if (!user) {
            req.flash("error", "用户不存在");
            return res.redirect("/");
        }
        proxy.emit("user", user);
    });
    Blog.get(req.params.user, function (err, blogs) {
        if (err) {
            req.flash("error", err);
            return res.redirect("/");
        }
        proxy.emit("blogs", blogs);
    });
});

User.get = function (username, callback) {
        db.collection("users", function (err, collection) {
            //处理
        });
}

把db.open放在最外面,然后在render的时候关闭

感觉一楼的方案比较好点,性能好点也比较灵活使用。

db打开了就不用关闭。因为node跟db的连接一直存在。不停打开关闭,性能并不好。

你遇到的问题是由于在User.getBlog.get方法中重复打开了数据库连接,导致出现错误db object already connecting, open cannot be called multiple times。为了更好地管理数据库连接,可以考虑使用单例模式来确保数据库连接只被打开一次。

解决方案

你可以通过引入一个全局的数据库连接对象来避免多次打开数据库连接。以下是修改后的代码示例:

// db.js
var MongoClient = require('mongodb').MongoClient;
var url = 'mongodb://localhost:27017/myproject';

var db;

module.exports = function () {
    if (!db) {
        MongoClient.connect(url, function (err, database) {
            if (err) throw err;
            db = database;
        });
    }
    return db;
};

接下来,在你的User.getBlog.get方法中使用这个单例数据库连接对象:

// User.get 方法
User.get = function (username, callback) {
    var db = require('./db')();
    db.collection("users", function (err, collection) {
        if (err) return callback(err);
        // 查询用户数据
    });
};

// Blog.get 方法
Blog.get = function (username, callback) {
    var db = require('./db')();
    db.collection('blogs', function (err, collection) {
        if (err) return callback(err);
        // 查询博客数据
    });
};

通过这种方式,你可以确保每次调用User.getBlog.get方法时都使用同一个数据库连接实例,从而避免了多次打开数据库连接的问题。

EventProxy 示例

如果你希望继续使用EventProxy,也可以按照以下方式调整你的代码:

var EventProxy = require('eventproxy');
var proxy = new EventProxy();

proxy.assign(['user', 'blogs'], function (user, blogs) {
    res.render("user", {title:user.name, blogs:blogs});
});

User.get(req.params.user, function (err, user) {
    if (!user) {
        req.flash("error", "用户不存在");
        return res.redirect("/");
    }
    proxy.emit('user', user);
});

Blog.get(req.params.user, function (err, blogs) {
    if (err) {
        req.flash("error", err);
        return res.redirect("/");
    }
    proxy.emit('blogs', blogs);
});

这样,EventProxy会帮你管理好异步操作的顺序,而不会出现深层回调的问题。

回到顶部