ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

javascript – Node.js,厄运的金字塔(即使是异步),你能写得更好吗?

2019-10-09 02:45:57  阅读:160  来源: 互联网

标签:async-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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有