Missing progress notifications when chaining jQuery promises - javascript

If I create a single Deferred object, then it reports progress like I would expect.
var d1 = function() {
var d = $.Deferred();
d.notify('d1');
d.resolve('d1');
return d.promise();
};
d1().progress(function(a) {
log('Progress: ' + a);
});
​
However, if I chain two Deferred objects using then(), the progress callbacks are not called.
d1().then(d1).progress(function(a) {
log('Progress: ' + a);
});
It seems to me that then() should propagate the progress notification, but it doesn't appear to be the case. Am I missing something?
I have tested this with jQuery 1.8.0 and 1.8.1. A full working example is here: http://jsfiddle.net/eWQuG/13/

I can see what's going on but it's slightly tricky to explain. You may have to read this through a couple of times ...
In Test 1, the promise passed to test() is the "original promise" ie. the object returned by d1(). Simple.
In Test 2, the object passed to test() is a new promise resulting from d1().then(d2).then(d3). Less simple. This new promise will have inherited only some of the original promise's properties - not including its progressCallbacks (as put in place in the original promise with notify()). This is reasonable for a promise returned by .then(), because .then() is designed to put in place its own doneCallbacks, failCallbacks and progressCallbacks, not to propagate those of the deferred/promise to its immediate left. In short, .then() does exactly what its name implies - it specifies what is to be done after its preceding deferred/promise has been processed.
You can better see what's going on by keeping a reference to the original promise (ie. the output of d1()), thus allowing the simple Test 1 to be run both before and after Test 2.
Here is a composite Test 3 :
log('-- Test 3 --');
var p = d1();//p is a promise
test(p);//equivalent to Test 1
test(p.then(d2).then(d3));//equivalent to Test 2
test(p);//equivalent to Test 1 again
DEMO
In the log, you will see that the behaviour of test(p) is identical in both cases. However, test(p.then(d2).then(d3)) behaves differently because the object passed is no longer the original promise but a new promise with no progressCallbacks.
EDIT
Ryan, something maybe worth considering in achieving what you want with :
d1().then(d2).progress(function(a) {
log('Progress: ' + a);
});
is that a the chain can be rearranged as follows :
d1().progress(function(a) {
log('Progress: ' + a);
}).then(d2);
The progress callback is thus applied to the original promise and because .progress() propagates the original promise down the chain, .then() will still act as before.
Both versions are valid but would be applicable in different circumstances.
If I'm right then maybe this is the short answer to your question.

You are doing a few weird things in your tests that are not really logical.
One, you are resolving the promise before you even return it, which defeat the purpose of promises. Promise callbacks are not supposed to be called on previously resolved promises, simple as that.
var p = $.Deferred();
p.then(function(p){log("ok")}).progress(function(p){log("progress " + p)});
p.notify('n1'); // will call handler and be logged
p.resolve();
p.notify('n2'); // will not call handler and will not be logged
Two, you are calling "notify" before you have set the progress callback, but that is somehow still working properly to a degree - it seems jQuery's particular implementation of notify is somewhat async... Except it fails with chaining. How or why, I don't know, but I don't think it is important to debug as your usage is improper anyway.
See this update to your fiddle:
http://jsfiddle.net/eWQuG/15/

Related

Are there promise library with serial and resolution without async-await?

The inconvenience I have is that there is no way, to my knowledge, using built-in Promise, to resolve a Promise within a synchronous block.
Specifically, the following code
var o = {};
function set(index, value) {
console.log(`setting ${index} to ${o.index = value}`);
}
Promise.resolve().then(()=>set('a', 1)).then(()=>set('b',2))
Promise.resolve().then(()=>set('c', 1)).then(()=>set('d',2))
console.log(`a is ${o.a}`);
Outputs :
a is undefined
setting a to 1
setting c to 1
setting b to 2
setting d to 2
Are there Promise like lib whose Promise would yield the following outputs:
setting a to 1
setting b to 2
setting c to 1
setting d to 2
a is 1
Obviously, no need to resort to Promises in the example above, but in my use case, setting 'a' leads to a pretreatment which only needs to be done once, whereas 'b' can be set multiple times, and is used within the same synchronous block it is set (no freedom on that).
I've looked up bluebird, and I have no clue how their promises work internally, but they seem to embrace using only promise chains.
Q Promise might be what I'm looking for, I'm not sure.
More generally, it feels daunting scouring the docs of the many Promise librairies out there querying some niche, depreciated need with likely the wrong keywords and concepts in mind.
This is definitely a case of ProblemXY where X could be solved in a variety of ways without Promises, for example by synchronous initializers, but I feel problem Y is interesting in and of itself (and likely a duplicate), so I'm still asking.
I'll get around giving problem X its own question, but long story short, I'd love to use, sometimes within a single js synchronous execution block, the expressive power of promises to pull values instead of pushing them and to flatten callbacks and try-catch pyramids.
Edit:
To clarify what I'm curious about, the way I would build the Promiselike with the desired properties is as follow :
let f = myPasync(function*(){ //equivalent to async function declaration
v = yield myPAwait(myPromiseInstance); //equivalent of v = await myPromiseInstance;
})
let pa = new myP( *(resolve, reject)=>{} ) //equivalent to new Promise((resolve, reject)=>{}) ; for simplicity, resolve and reject can be naked properties of pa
pa.myPthen((*()=>{})()) //equivalent to pa.then((v)=>{})
And then, instead of dynamically iterating over a task queue every tick, pushing thens and awaits of newly resolved promised to that queue in a FIFO manner,
I would push to the task queue in a LIFO manner and iterate over it upon any myP related function call.
So for example, when a promise is resolved, every routine awaiting it will resume before anything else, and in particular before the rest of the routine resolving the promise.
Other example when a routine awaits a promise, if that promise is already resolved, the routine is immediately resumed instead of letting other execute.

How do promise chains start and finish

I'm a little confused about how the sequencing works in various documents I've come across. For instance, I have seen this sort of thing
let p = Promise.resolve();
for (let x in something)
{
/* do some hairy time consuming code */
p = p.then(dosomething(x));
}
return p.then(finalthing()).catch(pretendnobadthing());
What I really don't understand is that if you do something that takes a large amount of time in the main code, wouldn't that mean that one of the promises could complete before the main code actually got round to to setting up the .then.
Can you always use .then/.catch on a promise, and if its already reached completion, it'll carry on from the point it got to, so it conceptually looks like a big chain that will run to completion at some indeterminate point? And if so, doesn't that mean every time you create a promise chain it'll hang about for ever?
Can you always use .then/.catch on a promise, and if its already reached completion, it'll carry on from the point it got to, so it conceptually looks like a big chain that will run to completion at some indeterminate point?
Yes, you can always use .then/.catch on a promise. When you call p.then(), there are three possibilities.
The promise p is still pending (not fulfilled or rejected yet). If that's the case, then the function reference(s) you passed to .then() are registered as listeners for that promise. So, when a future state transition happens on the promise (either going from pending => fulfilled or from pending => rejected), then the appropriate registered listeners will be called.
The promise p is already fulfilled. If that's the case, then calling .then(f1, f2) will schedule f1 to be called on the next tick (after the current piece of Javascript finishes executing) and it will be passed the saved resolved value.
The promise p is already rejected. If that's the case, then calling .then(f1, f2) will schedule f2 to be called on the next tick (after the current piece of Javascript finishes executing) and it will be passed the saved reject reason.
So, it's perfectly safe to call .then() on a promise that is already fulfilled or rejected. The appropriate listener will just be scheduled to run on the next tick.
The same logic applies to .catch() except it is only interested in cases 1 and 3 above.
Here are
And if so, doesn't that mean every time you create a promise chain it'll hang about for ever?
Promises are just objects like any other objects in Javascript. They will hang around only while other code still has some live reference to them. As soon as there is no longer any way to reach that promise object, they will be eligible for garbage collection just like any other objects in Javascript.
So, if you do:
var p = somePromiseReturningFunction();
p.then(f1).then(f2).then(f3);
Then, p will remain around until somePromiseReturningFunction() is done with any references to the promise that it returned (usually, though not always, this occurs when the promise is finally fulfilled or rejected) and when the variable p goes out of scope. If p never goes out of scope (like when it's global or in some other lasting scope), then it will remain forever (just like any other Javascript object).
There are some misconceptions in your question so let me attempt to square those up.
You're using the construct p = p.then(dosomething(x)); which is likely not correct. You need to pass .then() a function reference. So, unless you want doSomething(x) to execute immediately and it also returns another function that is what you want called as then .then() handler (which seems unlikely here), then this is not the right construct. You probably meant to have:
p = p.then(result => dosomething(x));
or in ES5 syntax:
p = p.then(function(result) {
return dosomething(x)
});
You show the same issue in this too:
return p.then(finalthing()).catch(pretendnobadthing());
which should probably be:
return p.then(finalthing).catch(pretendnobadthing);
Remember, when you use f(), that means to execute f immediately. When you just pass f, that passes a function reference which the underlying function/method you are passing it to can then call later at the time of its choosing which is what you want for .then() and .catch() handlers.
What I really don't understand is that if you do something that takes a large amount of time in the main code, wouldn't that mean that one of the promises could complete before the main code actually got round to to setting up the .then.
First off, my original explanation at the beginning of my answer should explain that calling .then() on an already resolved promise is perfectly fine so this isn't an issue at all. It will just schedule action on the next tick of the event loop.
But, that isn't even the case here because Javascript in the browser and node.js is single-threaded so while your long-running code is running, that promise (who's async action was previously started) can't yet get resolved. Though the underlying async operation may be done and an event may be sitting in the Javascript event queue that will trigger a callback that will resolve the promise, that event in the event queue won't get processed until the current piece of Javascript that is executing is done and returns control to the system.
What I really don't understand is that if you do something that takes a large amount of time in the main code, wouldn't that mean that one of the promises could complete before the main code actually got round to to setting up the .then.
Since common implementations of JavaScript never run it in parallel, no. Nevertheless,
Can you always use .then/.catch on a promise, and if its already reached completion, it'll carry on from the point it got to, so it conceptually looks like a big chain that will run to completion at some indeterminate point?
yes! This is a big advantage of promises. Take this for example:
var promise = Promise.resolve(5); // already resolved here
setTimeout(function () {
promise.then(function (x) {
console.log(x); // still logs 5
});
}, 1000);
And if so, doesn't that mean every time you create a promise chain it'll hang about for ever?
Until it’s resolved, yes, but if the promise has already been resolved and there are no ways to reference it anymore, it can be disposed of like any other object.

Multiple producers for a single resolver/promise

In the Q documentation there is this line:
"You can give the resolver to any number of producers and whoever resolves the promise first wins. Furthermore, none of the producers can observe that they lost unless you give them the promise part too."
I don't really get what that is saying. Is this for when you are constructing a promise by hand? Can someone give me an example please?
Is this for when you are constructing a promise by hand?
Exactly, you can create a promise with a deferred or the promise constructor, call resolve multiple times but only the first time is effectful. For example:
var p = new Q.Promise(function(resolve){
resolve(1);
resolve(2);
resolve(3);
resolve(4);
resolve(5); // can also pass `resolve` around.
});
p.then(function(result){
// always 1, all other calls had no effect.
});
This lets you build interesting things, for example let's build a race function that returns the result of the first to resolve of two promises (error handling omitted for brevity):
function race(p1, p2){
return new Promise(function(resolve){
p1.then(resolve);
p2.then(resolve);
});
}
Which would resolve with whichever promise is ready first, for example:
race(tryToGetFromFirstAPI(), tryToGetFromSecondAPI()).then(function(result){
// resolves as soon as the "fastest" resolved, with the result
});
Yes, I recently raised an eyebrow at that statement. Unfortunately you need already to understand Deferreds/Promises in order to understand what is actually meant.
For me, the problem starts with the introductory sentence :
Deferreds are cool because they separate the promise part from the resolver part. So:
This is misleading because Deferreds don't actually separate these components - it's more accurate to say that they combine them, but allow their separation if required.
Off the cuff, better wording would be :
Deferreds [are cool because they] include the means to derive a Promise and include, in the form of executable methods, the means to resolve or reject, all wrapped up in a single object. So:"
The sentence starting "You can give the promise to any number of consumers ..." is a bit wordy but otherwise fine.
But the sentence starting "You can give the resolver to any number of producers ..." would better read :
The resolve and/or reject methods are readily detachable allowing you, for example, to pass them to any number of producers and whoever resolves/rejects first wins; furthermore, by passing the detached methods and not the Deferred itself (and hence its Promise), none of the producers can observe whether they won or lost."
A third statement might also be included :
A Deferred can be passed in its entirety, complete with its means to resolve/reject and to derive a Promise."
In fact, all of the above can, with a little jiggery-pokery, also be achieved with the new Promise(synchronousSettlerFunction) construction and outer var(s).
It should also be said that statements like this can be reviewed and improved many times over. My attempts above took me 15 minutes so I wouldn't pretend they are the last word.

Wht is the difference between Q.all() and Promise.prototype.all() methods?

I'm trying to understand how promises work, so general idea is quite clear, but currently I'm stuck with all() method. I know, it used to make a promise for an array of other promises, which will be resolved when all promises from the array will resolved or will be rejected when any of the promises from the array will rejected. Here is my code snippet:
var qu = require('q');
var proArr = [];
for(var i = 0; i < 4; i++) {
var tmpDef = qu.defer();
(function(index, tmpDef) {
setTimeout(function() {
console.log('Timeout ' + index + ' has triggered!');
tmpDef.resolve();
}, (i + 1) * 1000);
proArr.push(tmpDef.promise);
})(i, tmpDef);
}
qu.all(proArr).then(function() {
console.log('All timeouts has passed with Q.all()!');
return 'some result';
});
qu.defer().promise.all(proArr).then(function() {
console.log('All timeouts has passed with promise.all()!');
return 'some result';
});
For this snippet a promise, which returned by qu.all() method will be resolved when all timeouts will be triggered, but a promise, which returned by qu.defer().promise.all() method will stay in pending state even if all timeouts will be triggered. So what the method Promise.prototype.all() have to be used for? And how it differs from Q.all() method?
Also I've looked in Q library sources, and here is a code for Promise.prototype.all() method:
Promise.prototype.all = function () {
return all(this);
};
As I can understand, this method calls Q.all() with an instance of current promise as an argument, but why? Don't the method Q.all() have to accept an array of promises? So I'll be very appreciated for clarification of all this moments.
Don't the method Q.all() have to accept an array of promises?
No, in fact the Q.all method also can take a promise for an array of promises. You can see that in the code well, it does call Q.when on the input. This might seem a bit useless, but it's a more forgiving API and apparently simiplifies the implementation of Promise.prototype.all.
What is the difference between Q.all() and Promise.prototype.all()?
Let's get back to our simpler mental model. Q.all is a static function that takes an array of promises and returns you a promise for an array of all results.
The .all prototype method is simply convenience. Instead of writing
….then(Q.all).…
you can use
….all().…
in a promise chain - these are exactly equivalent. Notice that the .all prototype method does not take any parameters - it does get the array from promise it is called on.
a promise, which returned by Q.defer().promise.all(proArr) method will stay in pending state even if all timeouts will be triggered
Yes. That's for two reasons:
Q.defer().promise is a promise that never resolves (and since you've thrown away the deferred, you never can). The chain just doesn't even advance to the .all(…) invocation.
As established above, the prototype method you're calling here doesn't take any arguments. The proArr is simply ignored.
If you want to use it, you can use the following though:
Q(proArr).all().…

Transform array of promises into array of values when fulfilled

I'm after a function that would return a resolved value of a promise. Failing gracefully is definitely a bonus, but it's an assumed precondition that when the function is called the promise is ready to be resolved.
While I'm working with webdriver.js promise implementation which allows the queue manipulations similar to below, I don't want to get too lost in semantics of queues/chains etc. For that reason alone, here is some pseudocode to cover what I'm trying to achieve:
var inputs = [...], outputs;
outputs = inputs.map(function(input){
//queue some async tasks to be performed with input
queue.enqueue(...);
//I can't return the *output* value here yet, naturally, so instead
return promise;
});
//now I'll add another task to the same queue
//this means that by the time this task is run
//the async tasks above would have been executed
//and the promises would be "resolvable"... right?
queue.enqueue(function(){
console.log(outputs); //>an array of promises
console.log(doSomeMagic(outputs)); //>resolved values as needed <<<
});
NB: afaik Q.all() would not do what I'm after - it takes an array of promises and returns a promise of an array, not its resolved value. I'm only happy to be proved wrong.
For anyone else looking for an answer based on the title of the question the following works with ES 2017+ to take an array of promises and return an array of values:
var arrayOfValues = await Promise.all(arrayOfPromises)
The only way to get the eventual value for a promise is with then. If a function performs work asynchronously, it must return a promise and under no circumstances can it return a plain value. To do so, it would necessarily have to block the thread of execution until the work completes, which is only possible with threads or fibers, which entrain the perils of deadlock and interleaving hazards.
As such, Q.all is in fact the method you need, except to follow up with then to get the eventual value.
Q.all(inputs.map(function (input) {
return promiseForOutput; // however you go about this
}))
.then(function (outputs) {
// at this event, outputs is an array of output values
});
There are ways to cheat, of course. promise.inspect() will return an object describing the state of the promise, like {state: "fulfilled", value: value} if it’s ready, or {state: "rejected", error} if it failed, or {state: "pending"}, if it is not yet ready. If, as you say, you are guaranteed that the outputs promise, returned by Q.all has been fulfilled, you can do this:
outputs = outputs.inspect().value
I don’t recommend this. The best way to know that a promise is resolved is to use then.
You could also just push values onto an outputs array of your making if you can also guarantee that all the outputs are ready through some external means.
var endResult = Q.defer();
var outputs = [];
inputs.forEach(function (input) {
outputPromise.then(function (output) {
outputs.push(output);
check();
}, endResult.reject);
});
check();
function check() {
if (outputs.length === inputs.length) {
// manipulate outputs directly, they are ready
endResult.resolve();
}
}
return endResult.promise;
The best means, however, is to just use Q.all(outputs).then to get an event that is guaranteed to be after all the outputs are ready.
Since you in general never know whether promises are resolved or not, you cannot simply transform them into a plain value. Q.all must return a promise since it cannot extract the values array from the async context. The only time you know that a promise has a value is in the success handler, and there you're getting the value anyway. You should not use another event system that tells you when a promise has settled - use the promise itself.
So instead of using queue.enqueue, just put Q.all(outputs).then(function(values){ /* do something */ }). However, if you cannot work around that, you might have a look at the Promise inspect debugging method: _.pluck(_.invoke(outputs, "inspect"), "value"). But notice that it might be easier not to store the values in promises then.

Categories