简体   繁体   中英

Node promise chain: Local functions and how to pass multiple values (code-review)

Considering following code I have following questions:

#1 The values returned by those bluebird.all functions need to be accessible in the later functions. From what I've read I could also use the this-context, without the necessity to declare the variables before. But I got some issues accessing those values in nested functions.

#2 Is spread the way to go in order to make those parameters accessible in subsequent functions?

#3 Since those functions are only working within the main method, should they be nested like this?

#4 I could not find any nicer solutions for conditional promise-chain execution. I'd prefer a less verbose solution, if there is any.

#5 As #3, but this time nested within nested function.

function create(contactRequestPayload) {

  // #1 Local variables vs this
  var
    contactRequest = {},
    property = {};

  return bluebird.all([
    new ContactRequestModel(contactRequestPayload).save(),
    getPropertyById(contactRequestPayload.propertyId),
    sendContactMail()
  ])
    // #2 Correct usage of spread
    .spread(function (_contactRequest, _property) {
      contactRequest = _contactRequest;
      property = _property;
    })
    .then(processFeedbackEmail)
    .then(updateModelState)
    .then(returnContactRequest)
    .catch(errorHandler);

  // #3 Nested functions
  function processFeedbackEmail() {
    // #4 Conditional code execution
    if (!_.get(property, "contact.email.feedback")) {
      return bluebird.resolve();
    }

    return createFeedbackObject()
      .then(convertToXml)
      .then(sendFeedbackMail)
      .catch(function (error) {
        logger.warn(error);
      });

    // #5 Nested functions within a nested function        
    function createFeedbackObject() {
      return contactRequestFeedbackObjectService.from(property, contactRequest);
    }

    function convertToXml(contactFeedbackObject) {
      contactRequest.feedbackJson = contactFeedbackObject;
      return objectToXmlService.convert(contactFeedbackObject);
    }

    function sendFeedbackMail(contactFeedbackXml) {
      contactRequest.feedbackXml = contactFeedbackXml;
      return contactRequestFeedbackMailService.send(property.contact.email.feedback, contactFeedbackXml);
    }
  }

  function returnContactRequest() {
    return contactRequest;
  }

  function sendContactMail() {
    return searchServiceClient.sendContactMail(contactRequestPayload);
  }

  function getPropertyById(propertyId) {
    return getPropertyServiceClient().fetchPropertyById(propertyId);
  }

  function updateModelState() {
    contactRequest.state = 'SENT';
    return contactRequest.save();
  }
}

Promise-chains let us sequence things as we would synchronous code, in a forward flow. I find that inlining functions regains important context. To use a previous variable, I use good old indentation:

function create(contactRequestPayload) {
  return bluebird.all([
    new ContactRequestModel(contactRequestPayload).save(),
    getPropertyServiceClient().fetchPropertyById(contactRequestPayload.propertyId),
    searchServiceClient.sendContactMail(contactRequestPayload)
  ])
  .spread(function processFeedbackEmail(contactRequest, property) {
    if (_.get(property, "contact.email.feedback")) {
      return contactRequestFeedbackObjectService.from(property, contactRequest)
      .then(function convertToXml(feedback) {
        contactRequest.feedbackJson = feedback;
        return objectToXmlService.convert(feedback);
      })
      .then(function sendFeedbackMail(xml) {
        contactRequest.feedbackXml = xml;
        return CRFeedbackMailService.send(property.contact.email.feedback, xml);
      })
      .catch(function (error) {
        logger.warn(error);
      });
    }
  })
  .then(function updateModelState() {
    contactRequest.state = 'SENT';
    return contactRequest.save();
  })
  .then(function returnContactRequest() {
    return contactRequest;
  })
  .catch(errorHandler);
}

(The function names are optional of course, but I kept them for reference.)

For the condition, I inverted it since returning undefined equals bluebird.resolve() .

Some might argue this is a question of style, but ES7 takes this analogy to its natural conclusion:

async function create(contactRequestPayload) {
  var [ contactRequest, property ] = await Promise.all([
    new ContactRequestModel(contactRequestPayload).save(),
    getPropertyServiceClient().fetchPropertyById(contactRequestPayload.propertyId),
    searchServiceClient.sendContactMail(contactRequestPayload)
  ]);
  if (_.get(property, "contact.email.feedback")) {
    try {
      var feedback = await contactRequestFeedbackObjectService.from(property,
                                                                    contactRequest);
      contactRequest.feedbackJson = feedback;
      contactRequest.feedbackXml = await objectToXmlService.convert(feedback);
      await CRFeedbackMailService.send(property.contact.email.feedback, xml);
    } catch (error) {
      logger.warn(error);
    }
  }
  contactRequest.state = 'SENT';
  await contactRequest.save();
  return contactRequest;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM