【问题标题】:Proper way to make callbacks async by wrapping them using `co`?通过使用`co`包装回调来使回调异步的正确方法?
【发布时间】:2016-06-08 20:45:54
【问题描述】:

现在是 2016 年,Node 从 v4 开始就几乎完全支持 ES6,Promises 从 0.12 开始就已经存在。是时候将回调抛诸脑后了。

我正在开发基于 commander.js 的 CLI 实用程序,它利用了许多异步操作 - http 请求和用户输入。我想将 Commander actions 包装在异步函数中,以便它们可以被视为 Promise,并支持生成器(对于我用于用户输入的 co-prompt 库很有用)。

我尝试用co 以两种方式包装CB:

1) program.command('myCmd') .action(program => co(function* (program) {...}) .catch(err => console.log(err.stack)) );

2)program.command('myCmd').action(co.wrap(function* (program) { .. }));

1)的问题是program参数没有传递

2) 的问题是错误被吞没了……

我真的很想让这个工作,因为它在我的用例中产生了更好的代码 - 涉及大量 http 请求,并且还使用 co-prompt 库等待用户输入..

以某种方式包装program.Command.prototype.action 是更好的选择吗?

谢谢!

【问题讨论】:

  • Commander v4.1 添加了 parseAsync 以支持异步操作处理程序

标签: node.js asynchronous generator co node-commander


【解决方案1】:

在 Commander.js 中有一个非常简单的方法来执行 async 函数

async function run() { 
  /* code goes here */  
}

program
  .command('gettime')
  .action(run);
  
program.parse(process.argv);

【讨论】:

    【解决方案2】:

    我使用了类似co 的定制版本来获取db.exec 函数,该函数使用yield 来执行数据库请求。您可以将参数传递给生成器函数(我传递了一个连接对象 - 请参阅我所做的注释)。

    这里是 db.exec 函数,它与 co 所做的非常相似

    exec(generator) {
      var self = this;
      var it;
      debug('In db.exec iterator');
      return new Promise((accept,reject) => {
        debug('In db.exec Promise');
        var myConnection;
        var onResult = lastPromiseResult => {
          debug('In db.exec onResult');
          var obj = it.next(lastPromiseResult);
          if (!obj.done) {
            debug('db.exec Iterator NOT done yet');
            obj.value.then(onResult,reject);
          } else {
            if (myConnection) {
              myConnection.release();
              debug('db.exec released connection');
            }
            accept(obj.value);
            debug('db.exec Promise Resolved with value %d',obj.value);
          }
        };
        self._connection().then(connection => {
          debug('db.exec got a connection');
          myConnection = connection;
          it = generator(connection); //This passes it into the generator
          onResult();  //starts the generator
        }).catch(error => {
          logger('database', 'Exec Function Error: ' + error.message);
          reject(error);
        });
      });
    }
    

    连接对象也由数据库连接对象包装,并提供生成器函数来处理来自数据库的结果的行,但我不会在这里发布(尽管下面的示例使用它来处理行) .

    这里是一个使用exec函数运行一段sql的例子

        db.exec(function*(connection) {
          if (params.name === ADMIN_USER) {
            debug('Admin Logon');
            user.name = ADMIN_DISPLAY;
            user.keys = 'A';
            user.uid = 0;
            let sql = 'SELECT passwordsalt FROM Admin WHERE AdminID = 0';
            connection.request(sql);
            yield connection.execSql(function*() {
              let row = yield;
              if (row) {
                user.nopass = (row[0].value === null);
              } else {
                user.nopass = false;
              }
              debug('Admin Password bypass ' + user.nopass.toString());
            });
          } else {
            debug('Normal User Logon');
            let sql = `SELECT u.UserID,PasswordSalt,DisplayName,AccessKey,l.LogID FROM Users u
              LEFT JOIN UserLog l ON u.userID = l.userID AND DATEDIFF(D,l.LogDate,GETDATE()) = 0
              WHERE u.UserName = @username`;
            let request = connection.request(sql);
            request.addParameter('username',db.TYPES.NVarChar,params.name);
            let count = yield connection.execSql(function*() {
              let row = yield;
              if (row) {
                user.uid = row[0].value;
                user.name = row[2].value;
                user.keys = (row[3].value  === null) ? '' : row[3].value;
                user.nopass = (row[1].value === null) ;
                user.lid = (row[4].value === null) ? 0 : row[4].value;
                debug('Found User with uid = %d and lid = %d, keys = %s',
                  user.uid, user.lid, user.keys);
              }
            });
            if (count === 0) {
              debug('Not Found User');
              // couldn't find name in database
              reply(false,false);
              return;
            }
          }
          if (!user.nopass) {
            debug('Need a Password');
            //user has a password so we must check it
            passGood = false; //assume false as we go into this
            let request = connection.request('CheckPassword');
            request.addParameter('UserID',db.TYPES.Int,user.uid);
            request.addParameter('password',db.TYPES.VarChar,params.password);
            yield connection.callProcedure(function*() {
              let row = yield;
              if (row) {
                //got a valid row means we have a valid password
                passGood = true;
    
              }
            });
          } else {
            passGood = true;
          }
          if (!passGood) {
            debug('Not a Good Pasword');
            reply(false,true);
          } else {
            if (user.uid !== 0 && user.lid === 0) {
              let sql = `INSERT INTO UserLog(UserID,LogDate,TimeOn,UserName) OUTPUT INSERTED.logID
               VALUES(@uid,GETDATE(),GETDATE(),@username)`;
              let request = connection.request(sql);
              request.addParameter('uid',db.TYPES.Int,user.uid);
              request.addParameter('username',db.TYPES.NVarChar,user.name);
              yield connection.execSql(function*() {
                let row = yield;
                if (row) {
                  user.lid = row[0].value;
                  debug('Users Log Entry = %d',user.lid);
                }
              });
            }
            reply(true,user);
          }
        })
        .catch((err) => {
          logger('database','Error on logon: ' + err.message);
          reply(false,false);
        });
      });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-26
      • 2018-04-06
      • 2021-06-26
      • 1970-01-01
      • 1970-01-01
      • 2014-06-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多