Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 简化循环和闭包中的嵌套承诺_Javascript_Loops_Promise_Nested_Closures - Fatal编程技术网

Javascript 简化循环和闭包中的嵌套承诺

Javascript 简化循环和闭包中的嵌套承诺,javascript,loops,promise,nested,closures,Javascript,Loops,Promise,Nested,Closures,我编写了一个大约50行的脚本来在MySQL数据库上执行内务管理。我担心我的代码会表现出反模式,因为它执行的简单函数会迅速升级为无法阅读的混乱状态 我想了解一些提高可读性的意见。 完整的脚本在这篇文章的底部给出了一个想法 关注问题 过度嵌套是由这样反复重复的模式造成的:(取自脚本的片段) 我在for循环和闭包中将一个承诺嵌套在另一个承诺之下。需要循环来迭代来自sql.query()的所有结果,并且需要闭包来将db的值传递给较低的值;如果没有闭包,循环甚至会在嵌套承诺执行之前完成,因此db始终只包含

我编写了一个大约50行的脚本来在MySQL数据库上执行内务管理。我担心我的代码会表现出反模式,因为它执行的简单函数会迅速升级为无法阅读的混乱状态

我想了解一些提高可读性的意见。
完整的脚本在这篇文章的底部给出了一个想法

关注问题 过度嵌套是由这样反复重复的模式造成的:(取自脚本的片段)

我在
for
循环和闭包中将一个承诺嵌套在另一个承诺之下。需要循环来迭代来自
sql.query()
的所有结果,并且需要闭包来将
db
的值传递给较低的值;如果没有闭包,循环甚至会在嵌套承诺执行之前完成,因此
db
始终只包含循环的最后一个元素,从而防止嵌套承诺读取
db
的每个值

全文 琐事 该脚本的目的是自动定期监视MySQL中存储在任何行、表和数据库中的任何记录是否接近其特定数据类型的超出范围限制。其他几个连接到MySQL的进程不断插入新的数值数据,其值和nonce不断增加;此脚本是检查此类数字限制的中心点。然后,脚本将被附加到Munin上,用于持续监控和警报


更新:修订脚本 正如@Kqcef所建议的那样,我将匿名函数从承诺嵌套中模块化,并使用
let
避免显式嵌套附加函数以保留变量上下文

这仍然过于冗长,之前我在Bash中用大约40行代码编写了相同的脚本,但性能要求为nodejs提供一个端口

"use strict";

var mysql     = require("promise-mysql");
var validator = require("mysql-validator");  // a simple library to validate against mysql data types

var ignoreDbs  = [ "information_schema" ],
    multiplier = 2,  // numeric records multiplier to check out-of-range proximity
    exitStatus = {'ok': 0, 'nearOutOfRange': 1, 'systemError': 2};

var mysqlHost = "localhost",
    mysqlUser = "btc",
    mysqlPass = "";

// return array of DBs strings
function getDatabases(sql) {
    return sql.query("show databases")
    .then(function(rows) {
        var dbs = [];

        for (var r of rows)
            dbs.push(r.Database);

        return dbs;
    });
}

// return array of tables strings
function getTables(sql, db) {
    return sql.query("show tables in " + db)
    .then(function(rows) {
        var tables = [];

        for (var r of rows)
            tables.push(r["Tables_in_" + db]);

        return tables;
    });
}

// return array of descriptions
function getTableDescription(sql, db, table) {
    return sql.query("describe " + db + "." + table)
    .then(function(rows) {
        var descrs = [];

        for (var r of rows) {
            descrs.push({ 'field': r.Field,    // eg: price
                          'type':  r.Type});   // eg: decimal(10,2)
        }

        return descrs;
    });
}

// return err object
function validateRecord(record, type) {
    var record, err;

    if (typeof record != "number") {
        console.log("error: record is not numeric.");
        process.exit(exitStatus.systemError);
    }

    // remove decimal part, only integer range is checked
    record = Math.trunc(record);
    err = validator.check(record * multiplier, type);

    return err;
}

(function() {
    var sql;

    mysql.createConnection({
        host:     mysqlHost,
        user:     mysqlUser,
        password: mysqlPass
    }).then(function(connection) {
        sql = connection;
    })

    .then(function() {
        return getDatabases(sql)
    })
    .then(function(dbs) {
        dbs.forEach(function(db) {
            if (ignoreDbs.indexOf(db) != -1) return;
            getTables(sql, db)
            .then(function(tables) {
                tables.forEach(function(table) {
                    getTableDescription(sql, db, table)
                    .then(function(descrs) {
                        descrs.forEach(function(descr) {
                            let field = descr.field,
                                type  = descr.type,
                                query = "select " + descr.field + " from " + db + "." + table + " ";

                            if (table != "nonce") query += "order by date desc limit 1000";

                            sql.query(query)
                            .then(function(rows) {
                                rows.forEach(function(row) {
                                    let err = validateRecord(row[field], type);
                                    if (err) {
                                        console.log(err.message);
                                        process.exit(exitStatus.nearOutOfRange);
                                    }
                                });
                            });
                        });
                    });
                });
            });
        });
    });


/*
    .then(function() {
        //if (sql != null) sql.end();
        //process.exit(exitStatus.ok);
    });
*/
})();

我同意Jaromanda的观点,即在for循环中使用
let
,以阻止值的作用域,并避免使用立即调用的函数,该函数虽然在功能上完全没有问题,但显然可读性较差

就最佳实践和避免反模式而言,在编写“好”代码方面,您可以争取的最重要的事情之一是构建模块化的、可重用的代码块。目前,您的代码有5或6个匿名函数,它们只存在于承诺回调链中。如果要将这些函数声明为该链之外的函数,不仅可以提高代码的可维护性(您可以测试每个代码),而且如果它们的名称清楚地指示了它们的用途,那么将形成一个非常可读的承诺链

(根据用户问题更新)

而不是离开内部功能

function getTableDescription(sql, db, table) {
    return sql.query("describe " + db + "." + table)
    .then(function(rows) {
        var descrs = [];

        for (var r of rows) {
            descrs.push({ 'field': r.Field,    // eg: price
                          'type':  r.Type});   // eg: decimal(10,2)
        }

        return descrs;
    });
}
…您可以轻松地将其剥离出来,以便您的代码能够自我记录:

function collectDescriptionsFromRows(rows) {
  var descriptions = [];
  for (var row of rows) {
    descriptions.push({'field': row.Field, 'type': row.Type});
  }
  return descriptions;
}

function getTableDescription(sql, db, table) {
    return sql.query("describe " + db + "." + table)
    .then(collectDescriptionsFromRows);
}
此外,如果您发现自己正在从一个阵列到另一个阵列进行数据收集,那么习惯使用内置的高阶函数(map、filter、reduce)将非常有帮助。与我刚才列出的
collectDescriptionsFromRows
不同,它可以简化为:

function collectDescriptionsFromRows(rows) {
  return rows.map(row => { 'field': row.Field, 'type':  row.Type});
}

更不冗长,更具可读性。如果继续提取链中的匿名函数,您的代码和承诺链将收缩,阅读起来更像是一个循序渐进的指令列表。任何你看到
功能的地方(
…都有更多的提取工作要做!你也可以造成一些伤害(积极的)通过提取您需要的所有数据,并使用本地逻辑将其归结为您需要的内容,而不是进行多次查询。希望这会有所帮助。

我同意Jaromanda的观点,即在for循环中使用
let
来阻止值的作用域,并避免使用立即调用的函数,这一点虽然非常好就功能而言,它的可读性明显较低

就最佳实践和避免反模式而言,在编写“好”代码方面,您可以争取的最重要的事情之一是构建模块化、可重用的代码块。目前,您的代码有5或6个匿名函数,它们只存在于承诺回调链中。如果您要将它们声明为Function在这条链之外,这不仅提高了代码的可维护性(您可以测试每个代码),而且,如果它们的名称清楚地表明了它们的用途,这将形成一个非常可读的承诺链

(根据用户问题更新)

而不是离开内部功能

function getTableDescription(sql, db, table) {
    return sql.query("describe " + db + "." + table)
    .then(function(rows) {
        var descrs = [];

        for (var r of rows) {
            descrs.push({ 'field': r.Field,    // eg: price
                          'type':  r.Type});   // eg: decimal(10,2)
        }

        return descrs;
    });
}
…您可以轻松地将其剥离出来,以便您的代码能够自我记录:

function collectDescriptionsFromRows(rows) {
  var descriptions = [];
  for (var row of rows) {
    descriptions.push({'field': row.Field, 'type': row.Type});
  }
  return descriptions;
}

function getTableDescription(sql, db, table) {
    return sql.query("describe " + db + "." + table)
    .then(collectDescriptionsFromRows);
}
此外,如果您发现自己正在从一个阵列到另一个阵列进行数据采集,那么习惯使用内置的高阶函数(映射、筛选、减少)将非常有帮助。与我刚才列出的
collectDescriptions fromRows
不同,它可以简化为:

function collectDescriptionsFromRows(rows) {
  return rows.map(row => { 'field': row.Field, 'type':  row.Type});
}

更不冗长,更具可读性。如果您继续提取链中的匿名函数,您的代码和承诺链将收缩,阅读起来更像是一个逐步的指令列表。无论您在哪里看到
函数(
…都有更多的提取工作要做!您还可能造成一些损害(积极的)通过提取您需要的所有数据,并使用本地逻辑将其归结为您需要的数据,而不是进行多次查询。希望这有所帮助。

我认为这个问题更适合您的主要问题可能是“同时”的数量请求发生。例如,如果有5个数据库,每个数据库有5个表,每个表有5个字段,那么可能会有125个“同时”
“从”+db+“+表中选择”+field+”
请求我认为这个问题更适合您的主要问题可能是“同时”的数量请求发生。例如,如果有5个数据库,每个数据库有5个表,每个表有5个字段,则可能会有125个“同时”
“从”+db+“+表中选择”+field+”