将 promise 与 redis 回调一起使用

Using promise with redis callback

我正在尝试用我的 Redis 服务器中的内容填充我的 var todos,我知道我必须使用承诺,但我可能不在正确的地方。

首先,我使用 .smembers() 函数获取所有 ID,对于每个 ID,我获取具有正确 ID 的对象并将其解析为 todos.

var todos=[];
res.locals.redis.smembers("todo:20", function(err, reply){ // i.e. SMEMBERS todo:20 returns 0 and 1
    var promises=reply.map(function(elem){

        res.locals.redis.get("todo:20:"+elem, function(err, reply1){ // i.e. GET todo:20:0
            return new Promise(function(resolve, reject){
                todos.push(JSON.parse(reply1));
                resolve();
            });
        });
    });

    Promise.all(promises)
    .then(function(){
        res.locals.redis.quit();
        res.render('todolist.ejs', {todo: todos});
    })
    .catch(function(reason){
        console.log(reason);
    });
});

如果要将接受回调的异步函数转换为returns promise 的函数,一般的做法是将函数包装在promise 中并传递resolve Promise 构造函数作为回调:

function getStuff(cb) {
  setTimeout(() => cb('stuff'), 1000);
}

function withPromise() {
  return new Promise(resolve => getStuff(resolve));
}

withPromise().then(console.log);

这意味着,与其将 promise 创建放在您的 redis 回调中,不如将其移出它:

res.locals.redis.get("todo:20:"+elem, function(err, reply1){ // i.e. GET todo:20:0
  return new Promise(...); // <-- move this outside of the callback
});

看起来像

var promises = reply.map(function(elem){
  return new Promise(function(resolve, reject){
    res.locals.redis.get("todo:20:"+elem, function(err, reply1) {
      todos.push(JSON.parse(reply1));
      resolve();
    });
  });
});

问题是您创建的承诺不在正确的位置。它必须在 map 函数内部创建,而不是在 redis.get 回调内部创建:

res.locals.redis.smembers("todo:20", function(err, reply) {
  var promises = reply.map(function(elem) {
    return new Promise(function(resolve, reject) {
      res.locals.redis.get("todo:20:" + elem, function(err, reply1) {
        let todo = JSON.parse(reply1);
        resolve(todo);
      });
    });
  });

  Promise
    .all(promises)
    .then(function(todos) {
      res.locals.redis.quit();
      res.render('todolist.ejs', { todo: todos });
    })
    .catch(function(reason){
      console.log(reason);
    });
});

但更好的解决方案是创建一个 promisify 函数,并将所有回调式函数转换为 promisified 函数:

let promisify = (fn, params) {
  return new Promise((resolve, reject) => {
    fn(params, (err, res) => {
      if (err) {
        reject(err);
      } else {
        resolve(res);
      }
    });
  });
};

promisify(res.locals.redis.smembers, 'todo:20')
  .then(reply => {
    let promises = reply.map(elem => promisify(res.locals.redis.get, "todo:20:" + elem);
    return Promise.all(promises);
  })
  .then(results => {
    let todos = results.map(item => JSON.parse(item));
    res.locals.redis.quit();
    res.render('todolist.ejs', { todo: todos });
  })
  .catch(err => console.log(err));