I have a function F that starts an asynchronous process X. The function returns a promise that is resolved when X ends (which I learn by means of a promise returned by X).
While the (w.l.o.g.) first instance of X, X1, is running, there may be more calls to F. Each of these will spawn a new instance of X, e.g. X2, X3, and so on.
Now, here's the difficulty: When X2 is created, depending on the state of X1, X1 should conclude or be aborted. X2 should start working only once X1 is not active any more. In any case, the unresolved promises returned from all previous calls to F should be resolved only once X2 has concluded, as well - or, any later instance of X, if F gets called again while X2 is running.
So far, the first call to F invokes $q.defer()
to created a deferred whose promise is returned by all calls to F until the last X has concluded. (Then, the deferred should be resolved and the field holding it should be reset to null, waiting for the next cluster of calls to F.)
Now, my issue is waiting until all instances of X have finished. I know that I could use $q.all
if I had the full list of X instances beforehand, but as I have to consider later calls to F, this is not a solution here. Ideally, I should probably then
-chain something to the promise returned by X to resolve the deferred, and "unchain" that function as soon as I chain it to a later instance of X.
I imagine that something like this:
var currentDeferred = null;
function F() {
if (!currentDeferred) {
currentDeferred = $q.defer();
}
// if previous X then "unchain" its promise handler
X().then(function () {
var curDef = currentDeferred;
currentDeferred = null;
curDef.resolve();
});
return currentDeferred.promise;
}
However, I don't know how to perform that "unchaining", if that is even the right solution.
How do I go about this? Am I missing some common pattern or even built-in feature of promises, or am I on the wrong track altogether?
To add a little context: F is called to load data (asynchronously) and updating some visual output. F returns a promise that should only be resolved once the visual output is updated to a stable state again (i.e. with no more updates pending).
currentDeferred
is gone, andcurrentDeferred
has been resolved (even though it should not if another instance of X is about to follow). – DeadeyeF
, not to the "internal" promises obtained from X. Thanks for the hint. – Deadeye[...] In any case, the unresolved promises returned from all previous calls to F should be resolved only once X2 has concluded, as well - or, any later instance of X, if F gets called again while X2 is running.[...]
is fishy, because it could lead to infinite pile up. – Narrowthen
-chain something to the promise, and later "unchain" that function" - yes, that's how it ideally would work, but unfortunately native promises do not support cancellation as "un-chaining" callbacks. You can however try my promise libraryCreed
that does support this usage, and here's an example that does exactly what you want. – AnzovinX1
is a special case - an "umbrella" for X2, X3 et seq - then you may be better off with a constructor that you call initially (with or withoutnew
) to obtain an instance, egvar foo = new Foo()
. Then, from the instance obtain your umbrella, egvar X1 = foo.promise()
. Then call some other method to obtain further promises, egvar X2 = foo.add()
. In addition to the two methods,Foo()
will consist primarily of the solution offered by T J Crowder. – Trotskyite