编程语言
首页 > 编程语言> > javascript – Node.js,厄运的金字塔(即使是异步),你能写得更好吗?

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