【问题标题】:Handling multiple requests in a loop causes sync issues在循环中处理多个请求会导致同步问题
【发布时间】:2017-12-23 18:32:40
【问题描述】:

我通过 post 请求调用 getLogs() 并从数据库中获取 LogFileID(filename) 列表,然后通过调用 _getLogFileUrls 传递此 LogFileID 以执行附加请求,这为我提供了该 ID 的签名 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);

        });         

    }    

【问题讨论】:

标签: javascript node.js asynccallback


【解决方案1】:

SQL 注入警报

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

答案

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

或者您可以使用带有良好模块的 Promise 来帮助处理并发,例如 Q 或 Bluebird:

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

或者你可以使用 ES8 async/await:

这些是处理像您这样的案件的主要方法。重新发明并发处理的轮子可能会导致(如您在此处看到的)容易出错且难以维护的代码。

我建议使用正确的工具来完成这项工作。

【讨论】:

  • 感谢您对 sql 注入的额外帮助。
  • 赞成是因为最重要的问题(即代码中的安全漏洞)已在顶部得到解决。好答案!
【解决方案2】:

使用异步/等待

安装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 = [];
    });
}

【讨论】:

  • 在您的示例中,您没有使用 asyncawait 模块中的 asyncawait 函数,即使您似乎使用了。这些函数需要括号来调用——比如async(function () { ...}),当你像async function () { ... }一样使用它时,你使用的是内置的async关键字,而不是你从asyncawait模块获得的函数。 await 也是如此。此外,如果您使用的是 async/await,那么如果您使用request-promise(或request-promise-native)而不是request,那么您的代码将简单得多,这样您就可以直接使用await
  • 你是对的@rsp,我忘了加括号
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-25
  • 1970-01-01
  • 2019-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多