2

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

我正在开发一个基于commander.js的CLI 工具,它利用了很多异步操作——http 请求和用户输入。我想将 Commander 包装action在异步函数中,以便它们可以被视为承诺,并且还支持生成器(对于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某种方式包装是一个更好的选择吗?

谢谢!

4

2 回答 2

0

我使用了一个定制版本的东西,比如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);
    });
  });
于 2016-02-25T22:23:32.817 回答
0

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

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

program
  .command('gettime')
  .action(run);
  
program.parse(process.argv);
于 2021-02-06T21:15:03.597 回答