gpt4 book ai didi

javascript - 简化循环和闭包中的嵌套 Promise

转载 作者:行者123 更新时间:2023-12-01 03:38:35 25 4
gpt4 key购买 nike

我编写了大约 50 行脚本来对 MySQL 数据库执行内务处理。我担心我的代码会表现出反模式,因为它所执行的简单功能会迅速升级为难以阅读的困惑。

我想要一些提高可读性的意见。
完整的脚本位于这篇文章的底部,以提供一个想法。

聚焦问题

过度嵌套是由这样的模式一遍又一遍地重复引起的:(摘自脚本的片段)

sql.query("show databases")
.then(function(rows) {
for (var r of rows) {
var db = r.Database;

(function(db) {
sql.query("show tables in " + db)
.then(function(rows) {
// [...]
}
})(db);
}
});

我将一个 promise 嵌套在 for 中的另一个 promise 之下。循环和闭包。需要循环来迭代 sql.query() 的所有结果,并且需要闭包才能传递 db 的值较低的 promise ;如果没有闭包,循环甚至会在嵌套的 Promise 执行之前完成,所以 db总是只包含循环的最后一个元素,防止嵌套的 Promise 读取 db 的每个值。 .

完整脚本

var mysql     = require("promise-mysql");
var validator = require("mysql-validator"); // 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};

(function() {
var sql,

mysqlHost = "localhost",
mysqlUser = "user",
mysqlPass = "";

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

.then(function() {
sql.query("show databases")
.then(function(rows) {
for (var r of rows) {
var db = r.Database;

if (ignoreDbs.indexOf(db) != -1) continue;
(function(db) {
sql.query("show tables in " + db)
.then(function(rows) {
for (var r of rows) {
var table = r["Tables_in_" + db];

(function(table) {
sql.query("describe " + db + "." + table)
.then(function(rows) {
for (var r of rows) {
(function(r) {
var field = r.Field,
type = r.Type, // eg: decimal(10,2)
query = "select " + field + " from " + db + "." + table + " ";

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

sql.query(query)
.then(function(rows) {
for (var r of rows) {
var record, err;

// remove decimal part, only integer range is checked
record = Math.trunc(r[field]);
err = validator.check(record * multiplier, type);
if (err) {
console.log(err.message);
process.exit(exitStatus.nearOutOfRange);
}
}
});
})(r);
}
});
})(table);
}
});
})(db);
}
});
})
.then(function() {
// if (sql != null) sql.end(); // may not exit process here: sql connection terminates before async functions above
//process.exit(exitStatus.ok); //
});
})();

琐事

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

<小时/>

更新:修改脚本

按照@Kqcef的建议,我将匿名函数从 promise 嵌套中模块化,并使用 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 个匿名函数,它们除了在您的 Promise 回调链中之外,别处都存在。如果您将它们声明为该链之外的函数,这不仅可以提高代码的可维护性(您可以测试每个单独的代码),而且如果它们的名称清楚地表明了它们的用途,那么这将使得代码非常可读 promise 链。

(根据用户问题更新)

而不是离开内部函数......

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

更加简洁,更具可读性。如果您继续提取链中的那些匿名函数,您的代码和 promise 链将缩小并且读起来更像是逐步的指令列表。无论您在哪里看到 function(...还有更多的提取工作要做!您还可以通过提取开始时需要的所有数据并使用本地逻辑将其归结为(积极地)造成一些损害您需要什么,而不是进行多次查询。希望这会有所帮助。

关于javascript - 简化循环和闭包中的嵌套 Promise,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44067998/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com