【问题标题】:Unhandled rejection Error: Can't set headers after they are sent. Node.js未处理的拒绝错误:发送后无法设置标头。节点.js
【发布时间】:2017-07-15 09:55:25
【问题描述】:

当尝试运行下面的代码时,我得到 Unhandled Rejection Error: Can't set headers after they are sentreturn 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 OpenRedisConnectionisRedisConnectionOpened,成员在 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()

标签: node.js promise


【解决方案1】:

这是我的看法:

somefile.js

var dbModules = require('../common/database');
var redisModules = require("../common/redismodule");

function getSettings(request, response) {
    function getData() {
        return redisModules.getRedisValue('key')
        .then(function (result) {
            return result || setData();
        });
    }

    function setData() {
        return dbModules.executeQuery('query')
        .then(function(results) {
            return redisModules.setRedisValue('key', results);
        });
    }

    return getData()
    .then(function(result) {
        response.status(200).send({ value: result });
    }).catch(function (e) {
        response.status(500).send();
    });
}

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);
        });
    }
};

redismodule.js

var Promise = require('bluebird');
var redis = Promise.promisifyAll(require('redis'));
var redisClient = null;

function openRedisConnection() {
    if (!redisClient || !redisClient.connected) {
        if (redisClient) {
            redisClient.end(); // End and open once more
        }
        redisClient = redis.createClient(6379, process.env.REDIS_URL, {
            auth_pass: process.env.REDIS_PASS
        });
        redisClient.selected_db = 1;
    }
    return redisClient;
}

module.exports = {
    'getRedisValue': function(key) {
        return openRedisConnection().getAsync(key); // here we call the promise-returning .getAsync() method, created by Promise.promisifyAll()
    },
    'setRedisValue': function(key, value) {
        return openRedisConnection().setAsync(key, value, 'EX', 1000); // here we call the promise-returning .setAsync() method, created by Promise.promisifyAll()
    }
};

我的主要贡献在somefile.jsredismodule.js。第三个模块database.js已经整理好了,仅此而已。

dbModules.executeQuery('query')redisModules.getRedisValue('key') 之类的问题需要解决,但我想你知道你在那里做什么。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-02
    • 2023-04-04
    • 2017-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多