0

I recently started using the q promise javascript library for my Node.js application. In my code I have a conditional that determines if I should execute 1 or 3 promise methods. I then want to execute the array of promise-returning methods and perform additional processing on the results.

Question: How do I use the results inside and outside .spread(...), so I can pass the result to other methods?

Some background: I am building a REST API, where depending on the values of the POSTed JSON body, I have to insert records into the DB using Sequelize first, get a response (so I know the auto-generated primary key), then insert records in other tables using that primary key.

var promiseMethods;

var someParam1, someParam2, someParam3 = 'Hello World';

if (someCondition == true) {

    promiseMethods = [promiseMethod1(someParam1)];
} else {
    promiseMethods = [
        promiseMethod1(someParam1),
        promiseMethod2(someParam2),
        promiseMethod3(someParam3)];
}

Q.all(promiseMethods)
    .spread(function(promseResult1,
                     promiseResult2,
                     promiseResult3) {

    var model = {
        result: promiseResult1
    };

    //Other code removed for brevity.

    return DatabasePromise.save(model)
});

I want to be able to then do something like the following hypothetical code.

var result = add(promiseResult1)
var result2 = subtract(promiseResult2)
var result3 = multiply(promiseResult3)

Essentially I want to execute the first 1 or 3 promise-returning methods in parallel, then be able to use the promise result(s) in multiple times thereafter.

Here is a better idea of what I want the code to do. The example here is that I have a product table, with users who are associated with that product. I want to insert data into these two tables (as well as a third table).

var _ = require('lodash');

function insert(request, reply) {

    var response;

    var db = getDBConnection();

    var step1Promises;

    if (property == true) {

        step1Promises = [
            CommonModule.promiseMethodForProductUsers(db, userUUIDs)
        ];

    } else {

        step1Promises = [
            CommonModule.promiseMethodForProductUsers(db, userUUIDs),
            CommonModule.promiseMethod2(payload.data1, payload.data2),
            CommonModule.promiseMethod3(db, payload.data3)
        ];
    }

    var step1 = Q.all(step1Promises);

    var resolvedProductUsers;
    var internalIDs;

    var productPromise = step1.spread(function(productUsers, promiseMethod2, promiseMethod3) {

        resolvedProductUsers = productUsers;

        var currentUser = users[0].uid;

        internalIDs = Q(_.pluck(resolvedProductUsers, 'uid'));

        var product = db.Product.build({
            product_name: productName,
            last_edit_user_id: currentUser
        });

        return CommonModule.createProduct(db, product);
    });

    // When running, internalIDs is undefined.
    var step2 = Q.all([
        CommonModule.buildProductUsersModel(db, internalIDs, productPromise.tid),
        CommonModule.buildOtherTableModel(db, productPromise.pid, currentUser.Id, otherData)
    ]);

    step2.spread(function(usersToInsert, otherTableDataToInsert) {

        CommonModule.saveProductUsers(db, usersToInsert);
        CommonModule.saveOtherTableData(db, otherTableDataToInsert);
    });

    // The response must be a promise, otherwise this insert method will finish
    // and return an empty result.
    response = productPromise;

    return reply(response);
}
musubi
  • 1,256
  • 3
  • 14
  • 22
  • What do you mean by "access outside of `.spread`"? How do you want to "pass the results to other methods"? What part of your code isn't working? – Bergi May 09 '15 at 16:37
  • You shouldn't name that array `promiseMethods`. As you *call* the single promise-returning *methods* in its construction already, it is an array of promises, not of methods. – Bergi May 09 '15 at 16:38
  • @Bergi, I updated my question for clarification. I hope it makes better sense. Regarding your comment about `promiseMethods`, should I write that part differently, or just change the name to something like `promises`? (Although this is purely example code, it is still helpful to understand how it all works.) – musubi May 09 '15 at 17:32
  • I know I can call the `add`, `subtract` and `multiply` methods inside `.spread`, but I want to avoid nesting too much. I have more code than just three simple methods to call. after `Q.all` is fulfilled. – musubi May 09 '15 at 17:39
  • You won't get around nesting those method calls in a `spread` or `then` callback. But normally you wouldn't need to nest multiple times. Can you please post what you want to do (maybe full code) while using "too much nesting", then I can show you how to avoid the nesting while achieving the same functionality. – Bergi May 09 '15 at 18:00
  • See my edits. Hopefully it makes sense :) – musubi May 09 '15 at 19:23
  • Ah, thanks. Personally, [I see nothing wrong with nesting](http://stackoverflow.com/a/28250687/1048572) here, but there are other solutions of course. – Bergi May 09 '15 at 20:03

1 Answers1

1

You can use normal variables to cache the results from the spread for further use.

var result1, result2, result3;
Q.all([
  method1,
  method2,
  method3
]).spread(function(
  promiseResult1,
  promiseResult2,
  promiseResult3
){
  result1 = promiseResult1;
  result2 = promiseResult2;
  result2 = promiseResult2;
  ...
  <do something with result1>
}).then(function) {
  // outside the spread
  <do something with result2>

  //
  <do something with result3>
})

You can also use nested closures to access results of a parent closure.

var result1, result2, result3;
Q.all([
  method1,
  method2,
  method3
]).spread(function(
  promiseResult1,
  promiseResult2,
  promiseResult3
){
  return doSomethingWhichReturnsPromise(promiseResult1)
  .then(function() {
     <do something with promiseResult2>
  })
  .catch(function() {
     <do something with promiseResult3>
  })
})
kumarharsh
  • 18,961
  • 8
  • 72
  • 100