javascript – Node.js,厄运的金字塔(即使是异步),你能写得更好吗?
作者:互联网
我认为自己是一个非常有经验的node.js开发人员.
然而我仍然想知道是否有更好的方法来编写以下代码,所以我没有得到厄运的金字塔…现在我对你很容易,我有一些代码,我的金字塔高达20层,没有开玩笑;那就是使用async.js!
问题实际上是我对预览变量有很多依赖,所以一切都必须嵌套.
写这本书“Async Javascript,用更少的代码构建更具响应性的应用程序”的人解释说,他会把功能放在根范围内,这肯定会摆脱金字塔,但现在你会有一大堆高范围变量(可能甚至全局,取决于你声明它们的范围),这种污染可能导致一些非常讨厌的错误(如果设置在全局空间,这可能导致与其他脚本的var冲突(确保你可以使用自调用函数,更多yachhh …甚至更糟糕的是,因为我们正在处理异步,可变的覆盖…).事实上,关闭的美丽已经很明显了.
他建议做的是:
function checkPassword(username, passwordGuess, callback) {
var passwordHash;
var queryStr = 'SELECT * FROM user WHERE username = ?';
db.query(selectUser, username, queryCallback);
function queryCallback(err, result) {
if (err) throw err;
passwordHash = result['password_hash'];
hash(passwordGuess, hashCallback);
}
function hashCallback(passwordGuessHash) {
callback(passwordHash === passwordGuessHash);
}
}
再次,不是一个干净的方法恕我直言.
所以,如果你看看我的代码(再次,这只是一个片段,我在其他地方变得更大的巢)你会经常看到我的代码越来越远离左边;这就是使用像瀑布和异步forEach这样的东西……
这是一个小例子:
ms.async.eachSeries(arrWords, function (key, asyncCallback) {
pg.connect(pgconn.dbserver('galaxy'), function (err, pgClient, pgCB) {
statement = "SELECT * FROM localization_strings WHERE local_id = 10 AND string_key = '" + key[0] + "'";
pgClient.query(statement, function (err, result) {
if (pgconn.handleError(err, pgCB, pgClient)) return;
// if key doesn't exist go ahead and insert it
if (result.rows.length == 0) {
statement = "SELECT nextval('last_resource_bundle_string_id')";
pgClient.query(statement, function (err, result) {
if (pgconn.handleError(err, pgCB, pgClient)) return;
var insertIdOffset = parseInt(result.rows[0].nextval);
statement = "INSERT INTO localization_strings (resource_bundle_string_id, string_key, string_revision, string_text,modified_date,local_id, bundle_id) VALUES ";
statement += " (" + insertIdOffset + ",'" + key[0] + "'," + 0 + ",'" + englishDictionary[key[0]] + "'," + 0 + ",10,20)";
ms.log(statement);
pgClient.query(statement, function (err, result) {
if (pgconn.handleError(err, pgCB, pgClient)) return;
pgCB();
asyncCallback();
});
});
}
pgCB();
asyncCallback();
});
});
});
在我的深度脚本中,我计算了超过25个右括号CRAZY,并记住了在哪里调用我的上一个callBack,所以async继续下一次迭代……
有这个问题的解决方案吗?或者它只是野兽的天真?
解决方法:
正如Mithon在他的回答中所说,承诺可以使这些代码更清晰,并有助于减少重复.假设您创建两个返回promises的包装函数,对应于您正在执行的两个数据库操作connectToDb和queryDb.然后你的代码可以写成:
ms.async.eachSeries(arrWords, function (key, asyncCallback) {
var stepState = {};
connectToDb('galaxy').then(function(connection) {
// Store the connection objects in stepState
stepState.pgClient = connection.pgClient;
stepState.pgCB = connection.pgCB;
// Send our first query across the connection
var statement = "SELECT * FROM localization_strings WHERE local_id = 10 AND string_key = '" + key[0] + "'";
return queryDb(stepState.pgClient, statement);
}).then(function (result) {
// If the result is empty, we need to send another 2-query sequence
if (result.rows.length == 0) {
var statement = "SELECT nextval('last_resource_bundle_string_id')";
return queryDb(stepState.pgClient, statement).then(function(result) {
var insertIdOffset = parseInt(result.rows[0].nextval);
var statement = "INSERT INTO localization_strings (resource_bundle_string_id, string_key, string_revision, string_text,modified_date,local_id, bundle_id) VALUES ";
statement += " (" + insertIdOffset + ",'" + key[0] + "'," + 0 + ",'" + englishDictionary[key[0]] + "'," + 0 + ",10,20)";
ms.log(statement);
return queryDb(stepState.pgClient, statement);
});
}
}).then(function (result) {
// Continue to the next step
stepState.pgCB();
asyncCallback();
}).fail(function (error) {
// Handle a database error from any operation in this step...
});
});
它仍然很复杂,但复杂性更易于管理.为每个“步骤”添加新的数据库操作不再需要新的缩进级别.另请注意,所有错误处理都在一个地方完成,而不是每次执行数据库操作时都必须添加if(pgconn.handleError(…))行.
更新:根据要求,以下是如何定义两个包装函数的方法.我假设您使用kriskowal/q作为您的承诺库:
function connectToDb(dbName) {
var deferred = Q.defer();
pg.connect(pgconn.dbserver(dbName), function (err, pgClient, pgCB) {
if (err) {
deferred.reject(err)
} else {
deferred.resolve({pgClient: pgClient, pgCB: pgCB})
}
});
return deferred.promise;
}
您可以使用此模式为任何需要一次性回调的函数创建包装器.
queryDb更直接,因为它的回调为您提供单个错误值或单个结果值,这意味着您可以使用q的内置makeNodeResolver实用程序方法来解析或拒绝延迟:
function queryDb(pgClient, statement) {
var deferred = Q.defer();
pgClient.query(statement, deferred.makeNodeResolver());
return deferred.promise;
}
有关承诺的更多信息,请查看我的书:Async JavaScript,由PragProg出版.
标签:async-js,javascript,node-js 来源: https://codeday.me/bug/20191009/1875891.html