Вызов функции обещания Q после вызова цепочки обещаний

Я работаю в приложении Node.js с библиотекой обещаний Q. У меня есть два набора цепочек обещаний: один для управления потоком, а другой для вызова методов службы, из которых я извлекаю данные. Мой вопрос в том, что мне нужно получить возвращаемое значение цепочки обещаний для моей другой цепочки обещаний.

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

В приведенном выше коде я вызываю bookService.getBookById(bookId) и получаю книгу. Затем я вызываю функцию bookDetals, которая представляет собой цепочку обещаний. Но моя проблема в том, что он возвращает returnValue до того, как цепочка промисов завершится. Как я могу получить возвращаемое значение цепочки обещаний (в строке (B)) для возврата на место (C). В настоящее время он возвращается раньше. поэтому на месте C написано undefined.


person CodeIntro    schedule 20.03.2017    source источник
comment
В вашей функции bookDetals (и во всех функциях обратного вызова внутри нее) отсутствует оператор return — без возврата обещания, как then может его ждать?!   -  person Bergi    schedule 20.03.2017
comment
О, и не используйте done — вам нужен только then.   -  person Bergi    schedule 20.03.2017
comment
См. stackoverflow.com/questions/22539815/   -  person Benjamin Gruenbaum    schedule 21.03.2017


Ответы (2)


Вам нужно вернуть обещание:

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


изменить:

deferred — это обсуждаемый антишаблон здесь. Честно говоря, лучше всего использовать polyfill, поскольку Promise входит в спецификацию es6.

person Joe    schedule 20.03.2017
comment
или, что предпочтительнее, просто return myService.retrieveATypeData .... etc, так как это является обещанием - person Jaromanda X; 21.03.2017
comment
stackoverflow.com/questions/23803743/ - person Benjamin Gruenbaum; 21.03.2017

Поскольку вы используете Node, я бы перешел к обещаниям ES6. Если ваша текущая версия еще не поддерживает промисы ES6, я бы рекомендовал вам переключиться на библиотеку (es6 -promise), который заполнит его для вас. С ES6 вы можете сделать что-то вроде этого:

// 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);
person Brian Lewis    schedule 20.03.2017
comment
stackoverflow.com/questions/23803743/ - person Benjamin Gruenbaum; 21.03.2017
comment
@BenjaminGruenbaum Возможно, вы пропустили комментарии в моем коде. Функции, которые я написал, предназначены просто для имитации API, возвращающего промис, который не считается анти-шаблоном. Реализация этих функций надежна и также не считается анти-шаблоном. - person Brian Lewis; 21.03.2017
comment
@BenjaminGruenbaum Было бы здорово, если бы вы могли убрать отрицательный голос. - person Brian Lewis; 22.03.2017
comment
const timeout = ms => new Promise(r => setTimeout(r, ms)) превратит все ваши функции в const getDetailsC => thing => timeout(ms).then(() => ({...thing, c: 'dishwasher')) и так далее. - person Benjamin Gruenbaum; 23.03.2017
comment
Это здорово, если бы я занимался оптимизацией моего фиктивного кода. Вы опять упускаете суть моего ответа. Вы сосредотачиваетесь на моих издевательствах, а не на цепочке обещаний, которая была фактическим ответом на вопрос. - person Brian Lewis; 23.03.2017
comment
Брайан, как человек, который некоторое время был на SO. Пусть отрицательные голоса подпитывают вас. Не давайте Бенджамину никакой власти. Пусть его отрицательный голос станет бессмысленным интернет-дерьмом. И, честно говоря, это 50/50, если Бенджамин вообще проголосовал против. Мне нравится твой ответ. Продолжайте отвечать! - person Joe; 25.03.2017
comment
@ Джо, согласен. Я просто хотел убедиться, что другие, кто может прочитать это, понимают, как этот парень упустил суть и не нашел времени, чтобы понять мой ответ. Спасибо! - person Brian Lewis; 27.03.2017