Jérôme B
Jérôme B

Reputation: 310

Using promise with redis callback

I'm trying to populate my var todos with what I have in my Redis server, I get that I have to use promises but I'm probably not in the right place.

First of all, I get all ids with .smembers() function and for each of the id I'm getting the object with the correct id and parsing it into 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);
    });
});

Upvotes: 2

Views: 5286

Answers (2)

alexmac
alexmac

Reputation: 19617

The problem is that you create a promise not in the correct place. It must be created inside of map function, not inside of redis.get callback:

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);
    });
});

But much better solution is to create a promisify function, and convert all callback-style functions to promisified functions:

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));

Upvotes: 1

nem035
nem035

Reputation: 35501

If you want to convert an async function that accepts a callback into a function that returns a promise, the general approach is to wrap the function in a promise and pass resolve provided by the Promise constructor as the callback:

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

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

withPromise().then(console.log);

This means that, instead of putting the promise creation within your redis callback, you should move it outside of it:

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
});

It would look something like

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();
    });
  });
});

Upvotes: 1

Related Questions