【发布时间】:2017-07-15 09:55:25
【问题描述】:
当尝试运行下面的代码时,我得到 Unhandled Rejection Error: Can't set headers after they are sent 当 return GetData(); 被调用时,它开始执行函数,但立即返回控件并返回错误。在调试过程中观察到。
基本上,如果 redis DB 中不存在,代码会尝试从 MySQL DB 中获取密钥
所有数据库和redis相关的模块都写在单独的文件中,以便重复使用。
somefile.js
var express = require('express');
var router = express.Router();
var dbModules = require('../common/database');
var redisModules = require("../common/redismodule");
function getSettings(request, response)
{
return GetData();
function GetData()
{
return redisModules.GetRedisValue("key")
.then(function (result)
{
if (!result)
return SetData();
else
return result;
})
.then(function (result)
{
response.status(200).send({ value : result });
})
.catch(function (e)
{
response.status(500).send();
};
}
function SetData()
{
return dbModules.executeQuery('query')
.then(function(results)
{
// some code
return 'some_key';
})
.then(function (result)
{
redisModules.setRedisValue('key', result);
});
}
}
database.js
用于处理数据库连接的文件
var mysql = require('promise-mysql');
pool = mysql.createPool({
host: '',
user: '',
password: '',
database: '',
connectionLimit: 4
});
module.exports = {
getSqlConnection: function()
{
return pool.getConnection().disposer(function(connection)
{
console.log("came here in releasing connection function");
pool.releaseConnection(connection);
});
},
executeQuery: function(sqlQuery)
{
return Promise.using(module.exports.getSqlConnection(), function(connection)
{
return connection.query(sqlQuery)
.then(function(results)
{
return results;
});
});
}
};
redismodule.js
用于处理 redis 获取、设置概念的文件
var Promise = require('bluebird');
var constants = require('../common/contants');
var redisClient; // Global (Avoids Duplicate Connections)
module.exports =
{
OpenRedisConnection : function()
{
if (redisClient == null)
{
redisClient = require("redis").createClient(6379, 'localhost');
}
},
isRedisConnectionOpened : function()
{
if (redisClient && redisClient.connected == true)
{
return true;
}
else
{
if(redisClient)
redisClient.end(); // End and open once more
module.exports.OpenRedisConnection();
return true;
}
},
GetRedisValue: function (key)
{
return new Promise(function (resolve, reject)
{
if(!module.exports.isRedisConnectionOpened())
reject("Redis connection failure");
redisClient.get(key, function (error, result)
{
if (error)
{
reject(error);
}
else
{
if (result == null)
resolve(); // Key not present so create
else
resolve(result);
}
});
});
},
SetRedisValue: function (key, value)
{
return new Promise(function (resolve, reject)
{
if(!module.exports.isRedisConnectionOpened())
reject("Redis connection failure");
redisClient.set(key, value, 'EX', 1000,
function(err,reply)
{
if (reply == 'OK')
resolve(value); // Send the value
else
reject(err);
});
});
}
};
调用getSettings函数时开始执行。
我刚刚包含了所有代码,如果它正确,它可能对其他人有用。
正确答案
somefile.js
var Promise = require('bluebird');
var dbModules = require('database');
var redisModules = Promise.promisifyAll(require("redismodule"));
async function getSettings(request, response) {
try {
var data = redisModules.GetRedisValue("key");
if (!data)
data = await SetData();
return response.status(200).send({
value: data
});
} catch (error) {
return response.status(500).send({
'error': 'Try after some time'
});
}
function SetData() {
let result = dbModules.executeQuery('query')
return redisModules.setRedisValue('key', result);
}
}
database.js
var mysql = require('promise-mysql');
var pool = mysql.createPool({
host: '',
user: '',
password: '',
database: '',
connectionLimit: 4
});
function getSqlConnection() {
return pool.getConnection().disposer(function (connection) {
console.log("came here in releasing connection function");
pool.releaseConnection(connection);
});
}
module.exports = {
executeQuery: function (sqlQuery) {
return Promise.using(getSqlConnection(), function (connection) {
return connection.query(sqlQuery)
.then(function (results) {
return results;
});
});
}
};
redismodule.js
var redisClient; // Global (Avoids Duplicate Connections)
// Making the below functions are private
function openRedisConnection() {
if (redisClient && redisClient.connected == true) {
return;
} else {
if (redisClient)
redisClient.end(); // End and open once more
redisClient = require("redis").createClient(6379,
process.env.REDIS_URL, {
auth_pass: process.env.REDIS_PASS
});
redisClient.selected_db = 1;
}
}
module.exports = {
GetRedisValue: function (key) {
openRedisConnection();
redisClient.get(key, function (error, result) {
if (error) {
return error;
} else {
if (result)
return result;
else
return null;
}
});
},
SetRedisValue: function (key, value) {
openRedisConnection();
redisClient.set(key, value, 'EX', 1000,
function (err, reply) {
if (reply == 'OK')
resolve(value); // Send the value
else
reject(err);
});
}
};
【问题讨论】:
-
好吧,如果出现错误,
catch错误处理程序和随后的then回调都会尝试发送响应。 -
嗨@Bergi,我甚至在它开始之前就尝试过调试 redisModules.GetRedisValue("key") 这行代码控制返回。我尝试使用断点、记录和观察
-
即使有
.then(...).catch(...)修复,代码仍然有问题,是吗?这就是我所看到的:在#somefile.js,(1)GetData()不返回任何东西——应该是一个承诺; (2)GetData()调用SetData()- 应该是setElection_Data(); (3)setElection_Data()不返回任何东西——应该是一个承诺。在database.js中,pool未本地化。在redismodule.js中,(1)new reject("...")应该是reject(new Error('...')); (2)OpenRedisConnection()和isRedisConnectionOpened()可能对模块保持私有。 -
Re
OpenRedisConnection和isRedisConnectionOpened,成员在 Node.js 中的作用域方式,在“必需”文件中声明的任何本地成员都不是全局成员(如果您习惯于编写客户端js)。相反,此类成员对于该文件定义的模块是私有的。只有通过module.exports公开的成员才能从模块外部公开访问。我的建议是这两个函数不必公开,因此将它们写为function OpenRedisConnection() {...}和function isRedisConnectionOpened() {...}并相应地调整对它们的调用。 -
另外:(1) 我会将
.then(...).catch(...)从GetData()移到return GetData();调用上。净效应是相同的,但GetData()现在只做这个,没有别的。 (2)openRedisConnection()和isRedisConnectionOpened()可能更好地合理化为一个函数getRedisConnestion(),它返回redisClient而不是true。 (3) 在bluebird.js的帮助下,你可以用Promise.promisifyAll(redisClient)对redisClient方法进行promisify 并简化redismodulel.GetRedisValue()和redismodulel.SetRedisValue()。