在循环中处理多个请求会导致同步问题

时间:2017-07-18 18:25:37

标签: javascript node.js asynccallback

我通过post请求调用getLogs()并从DB获取LogFileID(filename)列表,然后通过调用_getLogFileUrls传递此LogFileID来执行其他请求,这会给我一个签名的URL作为响应。我将所有这些逐个推入全局数组并返回结束响应。

我知道使用setTimeout不正确但问题没有使用,每次都会给我一个不同的结果。我该怎么做才能解决这个问题?我如何更正此代码,以便只有当签名的URL存储到全局数组中时,循环才会迭代到下一个。

function _getLogFileUrls(logFileId, callback){

var request = require('request'),
    config = require('../../config.js');    

var fileParams = {
    fileName: 'xyzdirectory/' + logFileId       
};
request.post({                    
          url: config.filesServiceUrl + 'get-logfile-urls',
          json: fileParams                            
        },function(error, response, body) {

        if (!error && response.statusCode === 200) {                                                                                
            callback(body);         
        } else {                    
            res.status(400).send('Error requesting file service for logs:');                                                
        }
    }).on('error', function(err) {
        console.log('File service error for Logs: ' + err);         
    });     
}

function getLogs(req, res){

        if(!req.body.id){
            return res.status(400).send('Please check the params!');
        }

        var date;

        if(req.body.date){
            date = req.body.date;
        } else {
            date = new Date().toISOString().slice(0,10);
        }       

        var sqlQuery = "SELECT `LogFileID` FROM `logs_data` WHERE `EmpID` = '" + req.body.id + "' AND DATE(`Timestamp`) = '" + date + "'",
        resArray= [];

        hitThisQueryForMe(sqlQuery, res, function(rows){

            if(!rows.length) res.json(rows);

            _.each(rows, function(item){            
                console.log('item: ' + item.LogFileID);
                _getLogFileUrls(item.LogFileID, function(response){
                    resArray.push(response);            
                });         
            });

            setTimeout(function(){          
                res.send(resArray);
                resArray = [];
            }, 4000);

        });         

    }    

2 个答案:

答案 0 :(得分:1)

SQL注入警报

首先,您的代码存在严重的SQL注入漏洞。切勿使用字符串连接来使用用户提供的数据创建SQL,否则任何人都可以读取,修改和删除数据库中的任何内容。这是一个非常严重的安全问题。有关详细信息,请参阅以下答案:

答案

现在回答你的问题。要处理你在这里尝试做的事情,你应该坚持回调并使用一个好的模块来处理像Async这样的并发:

或者您可以使用带有良好模块的promises来帮助实现Q或Bluebird等并发性:

此外,在使用promises时,您可以使用基于生成器的协程,使用co或Bluebird.coroutine等工具:

或者您可以使用ES8 async / await

这些是处理像你这样的案件的主要方式。重新发明并发处理的轮子可能会导致(如您所见)易于出错,难以维护的代码。

我建议使用合适的工具。

答案 1 :(得分:0)

使用async / await

安装 asyncawait 库及其依赖 bluebird

npm install asyncawait --save
npm install bluebird --save

您编辑的代码应如下所示:

const async = require('asyncawait/async');
const await = require('asyncawait/await');
const Promise = require('bluebird');
const request = require('request');
const config = require('../../config.js');

function _getLogFileUrls(logFileId) {
    return new Promise((resolve, reject) => {
        var fileParams = {
            fileName: 'xyzdirectory/' + logFileId
        };

        request.post({
            url: config.filesServiceUrl + 'get-logfile-urls',
            json: fileParams
        }, function (error, response, body) {
            if (!error && response.statusCode === 200) {
                resolve(body);
            } else {
                reject('Error requesting file service for logs:');
            }
        }).on('error', function (err) {
            console.log('File service error for Logs: ' + err);
        });
    });
}

function getLogs(req, res) {

    if (!req.body.id) {
        return res.status(400).send('Please check the params!');
    }

    var date;

    if (req.body.date) {
        date = req.body.date;
    } else {
        date = new Date().toISOString().slice(0, 10);
    }

    var sqlQuery = "SELECT `LogFileID` FROM `logs_data` WHERE `EmpID` = '" + req.body.id + "' AND DATE(`Timestamp`) = '" + date + "'",
        resArray = [];

    hitThisQueryForMe(sqlQuery, res, function (rows) {

        if (!rows.length) res.json(rows);

        _.each(rows, (async function (item) {
            console.log('item: ' + item.LogFileID);
            var logFileUrlResponse = await (_getLogFileUrls(item.LogFileID));
            resArray.push(logFileUrlResponse);
        }));

        res.send(resArray);
        resArray = [];
    });
}