Pergunta

I've got trouble wrapping my head around a certain problem:

In my data-flow app (a visual editor), I have both autonomous objects which communicate though ports via unordered simultaneous messages and thus represent an asynchronous system.

in this picture, f would receive x from the first object, transform and immediately send the transformed value to the next

Still, I also want to use functions which are synchronous, e.g they need input to compute it's value

Now the problem is - how do I combine both with a non-trivial system? 2

In this picture, f needs x and y to compute its value, but during any point in time, only one input may be present (suppose x is present, y is not). Generalize this to higher dimensions

I can see a possible way out:

  • make x an array X = (x,y) and combine the first two objects resulting in a Type-1 like system ..

  • or make f an active object, which caches x,y and then computes

  • or make f always compute its value (e.g when only x is present, compute x*y = x*0 ...)


So the possibility of futures was mentioned. I'M using Java, so i'll frame my possible solution via Objects

X is sent, y is missing - loading= create future (maybe a copy of f) waiting for y"> X is sent, y is missing -> create future (maybe a copy of f) waiting for y

enter image description here

enter image description here

Foi útil?

Solução

Promises were made to solve problems like this; they work really well in functional languages (I've personally used them extensively in Javascript, where curiously jQuery actually has the worst implementation of them - see this comparison). The weird thing about using promises is accepting that things are easier when you make everything use them. I will frame my answer in JavaScript terms using the promise library Q, since that's what I know best - apologies if that's not your cup o' tea, and double apologies for answering a language agnostic question with a specific language and library to boot.

A promise is a wrapper for a callback function. In many libraries, this is called a .then() function. In simplest terms, a promise waits for a return, and then calls the function passed to then(). The simplicity was confusing for me in the beginning, so lets use your example. Let me upgrade x and y to functions. If they are functions that return promises (as many asynch heavy libraries do, like a NodeJS database accessor), this becomes trivial.

Q.all([x(), y()]).then(function(results) {
  // Do some stuff with results, which is an enumerable collection containing
  // the in-order results of the promises passed in.
});

The trouble is, what if x() and y() don't return promises? As I mentioned, your life gets easier if you make them return promises. Fortunately, any half-decent promise library provides tools for this as well. In fact, there are several ways to do this in Q.

// Upgrade x into a promise returning function
function promise_x(/*x params...*/) {
  return Q.fcall(x(/*x params...*/));
}

// Upgrade x into a promise with a deferred
// Useful if you have some standard error handling specific to x-like functions
function defer_x(/*x params...*/) {
  var deferred = Q.defer();
  // If x is asynch, assume it takes a callback function with params of (error, result)
  x(/*x params...*/), function (error, result) {
    if (error) {
      deferred.reject(new Error(error));
    } else {
      // If you have some processing to do with the result of x (and not y), you can do
      // it here and return the modified result instead
      deferred.resolve(result);
    }
  });
  return deferred.promise;
}

If you repeat that code for the y() function, you get back to where you can use Q.all() to wait for both x() and y() to complete.

Q also provides an API for creating promises, which is like a wrapper function for Q.defer().

However, the best feature a promise library provides is aggregated error handling. Expanding on our first Q.all example, what happens if x or y throw an error? As that code is written now, it gets lost. Consider the following:

MyOverallFunction() {
  Q.all([x(), y()]).then(function(results) {
    // Do some stuff with results, which is an enumerable collection containing
    // the in-order results of the promises passed in.
    var combinedResult = synchronousFunction(result[0], result[1]);
    anotherAsyncPromiseFn(combinedResult)
      .then(function(result)) {
        return result;
      });
  }).then(function(result) {
     // Here, result is the return value of anotherAsyncPromiseFn
  }).error(function(err) {
     // This is the cool part. Any error from x, y, synchronousFunction, or
     // anotherAsyncPromiseFn will propagate to here. If you don't want to
     // handle it, just re-throw it (after logging it, if you prefer).
  });
}

You'll also notice in the above example there are several places where combining sync and async methods is demonstrated.

Ultimately, the tools that are at your disposal depend upon your language and environment. Promises have been implemented in more languages than just JS, and another closely related idiom from async/functional programming is Futures. Admittedly, if your language of choice does not have a Promise library, this answer will be pretty useless to you, but hopefully you at least find it interesting.

Update: Now that you mention Java, it looks like there is a decent promise library in JDefered. I've not used it, but it looks like my examples could be applied with it in a fairly straightforward manner.

Outras dicas

What you are describing is a common issue in reactive programming. The idiom is for asynchronous code to produce a Future which acts as a rendezvous point for other code. The code that depends on the results uses call-backs (or call-forwards, depending on your point of view), which take the value as it will be produced in the future.

With suitable language support (Erlang, Scala), the use of these Futures becomes quite simple and straightforward. You might also want to investigate Actors in Akka. This provides a toolkit for much more complex systems.

Licenciado em: CC-BY-SA com atribuição
scroll top