0

I'm working on a Node.js function that will make three concurrent calls to an Amazon DynamoDB: put (store some info), query (get some info), and update (conditionally update some info). The calls do not depend on each other.

Amazon's SDK can invoke them asynchronously using either callbacks:

var docClient = new AWS.DynamoDB.DocumentClient();
docClient.put(params1, callback);
docClient.query(params2, callback);
docClient.update(params3, callback);

or with promises:

var promise1 = docClient.put(params1).promise();
var promise2 = docClient.query(params2).promise();
var promise3 = docClient.update(params3).promise();

I want to process the results of the query call, but only after the other two calls have settled. I don't care in what order they happen, or if they are successful.

Promise.all() would do the trick, except for the fact that it is likely that the update will throw an exception (Amazon treats conditional updates that don't meet their condition as exceptions).

How would you solve this?

justkevin
  • 5
  • 1
  • thanks for accepting the answer, but it was before my edit. Hope you like the second approach better, I think it's superior. – Evert Aug 31 '17 at 01:52

1 Answers1

1

It's not terribly hard to write your own variant of Promise.all() that returns every result, and perhaps null if any of them failed. Example:

// Sorry, completely blanking on a better function name.
function myAll(promises) {

  return new Promise((res, rej) => {
    var settled = 0;
    var results = [];
    promises.forEach(function(promise, index) {
      promise.then( result => {
        results[index] = result;
      }).catch( err => {
        results[index] = null
      }).then( () => {
         settled++;
         if (promises.length === settles) {
            res(results);
         }
      });
    });

  });
}

The above function basically calls then on every promise, stores all the results and once it has all the results, it will return the top promise. This is very similar to how Promise.all(); is implemented.

However, I'm not entirely sure if that's how I'd implement it. For one, errors are all converted to null and more or less 'lost'. I think this is not good design, because it's a good idea to just catch the specific exception that you expect. Lets assume for instance that your client throws a PreconditionFailed exception, and you only care about that for the update call. It's super easy to just catch that.

Example:

Promise.all([
  docClient.put(params1).promise(),
  docClient.query(params2).promise(),
  async () => {
    try {
      return docClient.update(params3).promise()
    } catch (e) {
      if (!(e instanceof PreconditionFailedException)) {
        // rethrow
        throw e;
      }
      return null;
    }
  }
]);
Evert
  • 93,428
  • 18
  • 118
  • 189