0

I am working in a Node.js app with Q promise library. I have two set of promise chains and one is for controlling the flow and one for calling service methods where I retrieve data from, My question is, I need to get the return value of the promise chain to my other promise chain.

MyExample.js

bookService.getBookById(bookId)
  .then(bookDetals)
  .then(function(returnValue) { // <- (A)
    res.send(200, returnValue); // <- (C)
    return returnValue;
  }).catch(function(error) {
    logger.error('Error getting values');
    res.send(500, error);
  });


bookDetals = function(book) {
  myService.retrieveATypeData(book, bookId)
    .then(function(bookData) {
      myService.retrieveBTypeData(bookId)
        .then(function(bdata) {
          bookData[bTypeData] = bdata;
          myService.retrieveCTypeData(bookId)
            .then(function(cdata) {
              bookData[cTypeData] = cdata;
            }).done(function() {
              return bookData; // <- (B)
            })
        });
    });
};

In the above code, I am calling bookService.getBookById(bookId) and getting the book. Then I am calling bookDetals function which is a promise chain. But my problem is it returns the returnValue before the promise chains over. How can I get the return value of promise chain (in line (B)) to return in place (C). Currently it return before. so in place C it says undefined.

vesse
  • 4,871
  • 26
  • 35
CodeIntro
  • 191
  • 1
  • 1
  • 10

2 Answers2

0

Since you are using Node, I would move towards ES6 Promises. If your current version does not yet support ES6 Promises, I would recommend you switch over to a library (es6-promise) that polyfills it for you. With ES6, you could do something like this:

// mock async promise
const getThing = id => (
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({
        id
      });
    }, 250);
  })
);

// mock async promise
const getDetailsA = thing => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(Object.assign({}, thing, {
        a: 'purple'
      }));
    }, 250);
  })
};

// mock async promise
const getDetailsB = thing => (
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(Object.assign({}, thing, {
        b: 'monkey'
      }));
    }, 250);
  })
);

// mock async promise
const getDetailsC = thing => (
  new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(Object.assign({}, thing, {
        c: 'dishwasher'
      }));
    }, 250);
  })
);

getThing('123')
  .then(getDetailsA)
  .then(getDetailsB)
  .then(getDetailsC)
  .then(console.log)
  .catch(console.error);
Brian Lewis
  • 5,739
  • 1
  • 21
  • 28
  • http://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it/23803744#23803744 – Benjamin Gruenbaum Mar 20 '17 at 23:02
  • @BenjaminGruenbaum Perhaps you missed the comments in my code. The functions I have written are merely to "mock" an API that returns a promise, which is not considered an anti-pattern. The implementation of those functions is sound and is also not considered an anti-pattern. – Brian Lewis Mar 21 '17 at 15:33
  • @BenjaminGruenbaum It would be great if you could remove the downvote. – Brian Lewis Mar 22 '17 at 15:15
  • `const timeout = ms => new Promise(r => setTimeout(r, ms))` would make all your functions into `const getDetailsC => thing => timeout(ms).then(() => ({...thing, c: 'dishwasher'))` and so on. – Benjamin Gruenbaum Mar 22 '17 at 23:53
  • That is great if I was concerned with optimizing my mock code. Again, you are missing the point of my answer. You are focusing on my mocks and not the chaining of the promises, which was the actual answer to the question. – Brian Lewis Mar 23 '17 at 19:42
  • Brian, as someone who's been on SO for awhile. Let the downvotes fuel you. Dont give Benjamin any power. Let his downvote become the meaningless internet crap it is. And truthfully, it's 50/50 if benjamin even did the downvote. I like your answer. Keep answering! – Joe Mar 25 '17 at 02:44
  • @Joe, Agreed. I just wanted to make sure that others who might read this understand how this guy was missing the point and didn't take the time to comprehend my answer. Thanks! – Brian Lewis Mar 27 '17 at 19:17
-1

You need to return a promise:

bookDetals = function(book) {
  return Q.Promise(function(resolve, reject, notify) {
    myService.retrieveATypeData(book, bookId)
        .then(function(bookData) {
          myService.retrieveBTypeData(bookId)
            .then(function(bdata) {
              bookData[bTypeData] = bdata;
              myService.retrieveCTypeData(bookId)
                .then(function(cdata) {
                  bookData[cTypeData] = cdata;
                }).done(function() {
                  resolve(bookData); // <- (B)
                })
            });
        });
  }  
}


edit:

deferred is an anti-pattern discussed here. Honestly, it might be best to use a polyfill since Promise is in the es6 spec.

Community
  • 1
  • 1
Joe
  • 80,724
  • 18
  • 127
  • 145