Background
Started with 2 nearly identical javascripts then refactored into 3: a utility script containing formerly redundant code + 2 calling scripts.
Problem
The two original scripts use jquery Deferreds (when.then) and work fine. The 3-script scenario fails because the next-to-last promise resolves early from one calling script.
Details
The first script, call it "multi" uses a sequence of deferreds to loop through a series of ajax requests and then refreshes the "multi" page in the browser. So:
auth -> user -> loop (updateIssue -> transition) end loop -> refresh
The 2nd, "single" uses effectively the same code without the loop, and then a different refresh function
My aim was to refactor the code into a promise chain in a utility script and use like so:
// calling script
Utility.promiseChain().done(refresh())
// utility script
Utility.promiseChain = function() {
return authPromise()
.then(function() { return userPromise();} )
.then(function() { return Promise.all(array of update.then(transition) promises);})}
The Problem, More Specifically
The Promise.all call ALWAYS resolves after update, but before transition, causing the refresh to fire early, followed by the transition. Any insight you can provide will be most helpful.
The Promise.all call ALWAYS resolves after update, but before transition
The only way that can happen is if something in your chain of events is not properly chaining promises. You will have to show us the real code for us to help you identify the specific problem, but if Promise.all() is not waiting for a transition promise, then that promise must not be properly chained into the array of promises that Promise.all() is waiting on.
Specifically, we need to see the code behind this part of your pseudo-code:
array of update.then(transition) promises
to see exactly how transition() is involved in creating that array of promises.
As always, if you show us your REAL code rather than just pseudo-code, we can help find the source of the actual error in programming logic.
A possible scenario is that you are using JQuery < version 3, and the call to authPromise() returns a JQuery promise. This would mean all the promises returned by then in the promise chain returned by Utility.promiseChain are JQuery promises. The last time I checked, older versions of JQuery do not recognize foreign library promises, including those returned by native Promise constructors, as a promise and will fulfill a JQuery promise resolved with one without waiting for it to be settled.
It is possible that the promise object returned by Promise.all in
.then(function() { return Promise.all( // ...
is being used to fulfill the promise returned by then without waiting for it to be settled.
In short you can't resolve an older version of JQuery promise with an ES6 promise and produce the effect of resolving a promise with a promise.
Potential solutions:
Upgrade to JQuery 3 (you lose support for old versions of IE but these don't support Promise anyway). Recommended if you can do it.
Convert all promise usage to ES6 style promises and convert any JQuery promises that are being passed around to ES6 promises before use by calling Promise.resolve( jqPromise)
Write the equivalent of Promise.resolve in reverse to convert an ES6 promise into a JQuery promise. I would rate this as possible but a huge hack backwards in the wrong direction.
Related
Thank you for reading this.
I'm really struggling with fully understanding how async programming works in Javascript.
Imagine this code:
Promise.resolve().then(randomcallback)
So in this scenario what I think would happen is the following: Firstly, in the normal call stack the Promise.resolve() function would immediately resolve a Promise object and return it. Then, the .then() function checks if the promise is resolved and if so it adds the randomcallback function to the microtask queue which when the call stack is empty gets executed and returns a new promise object.
But imagine if instead of calling the .then() function on a resolved promise object, we call it on a pending object. How is the then function able to check when the Promise object is resolved? Is it constantly checking in some web api thread like some event listener (since the rest of the program can run on) for the status property to be changed to fulfilled to then pass the callback function to the microtask queue?
The same for async await, does the await keyword just contact some web api to listen when the promise gets resolved? If so does it still do that if the promise object is already resolved at the beginning (no settimeout in it)
if you use:
randomunresolvedpromise.then(randomcallback)
let's say that randomunresolvedpromise gets fulfilled in 3 seconds (when callstack is empty)
how is the .then function even executed (also I've looked at the source code of the library, but it's really confusing since it uses so many callback functions) since the program literally keeps running and randomresolvedpromise only gets fulfilled when the stack is empty, is the function also just waiting in some sort of web api or what?
I've literally watched a lot of videos, read a lot of articles, but they don't seem to touch what really goes on in the event loop when using Promises and async and await.
It's all really confusing to me, I hope my question really makes any sense.
I really appreciate any answer, thanks!
How is the then function able to check when the Promise object is resolved?
It doesn't. The then method puts the callback on the promise's list of fulfillment callbacks, and then its job is complete. Later, when the promise is fulfilled, it's that code (fulfilling the promise) that puts calls to the promise's fulfillment callbacks in the microtask queue. (You're right though that it's then that does it when the promise is already fulfilled when then is called.)
await works the same way, since await is "just" syntactic sugar for then. (That "just" skips over a lot of details, though. :-D )
Side note: You'll notice I said "fulfillment callbacks," not "resolution callbacks," and referred to the promise being fulfilled (not resolved). There's a big difference between "fulfilled" and "resolved," but unfortunately it's not well understood and people use "resolve" where they mean "fulfill" a lot. See my blog post here (which the MDN folks linked from their documentation) for the difference (and why it matters). (That said, if you say you "resolve the promise with X" and X isn't a promise or other thenable, that's basically synonymous with "fulfill the promise with X." That's not true when X is a promise or other thenable.)
async function fetchUserCollection(callback)
{
await client.connect();
const userCollection = client.db('database').collection('users');
await callback(userCollection);
client.close();
}
I'm trying to move away from callbacks and towards Promises.
However await resolve(userCollection) would not work here if this was a promise as resolve() returns immediately.
Is there a way to use promises here? or is a callback necessary here?
Returning a plain Promise won't work here, because a plain Promise is mostly indifferent to the .thens that may be called on it later. All that a Promise can do with .thens called on it is to execute them once the Promise resolves, and that's it. Spec-compliant Promises don't have a functionality to run something after something finishes consuming them with .then.
I think what you're doing currently is the cleanest solution there is. Despite the fact that Promises exist, callbacks aren't inherently bad - sometimes there are use cases for them, like here. The main issue with callbacks IMO is when they get nested in each other, and a lot of ugly indentation and hard-to-read code results, but that's not going on in your code.
According to Promise - Javascript | MDN,
The constructor is primarily used to wrap functions that do not
already support promises.
At a low level, the functions that already support promises would
be doing this right i.e constructing a promise using Promise and returning it back?
Is there a better way to create a promise other than using the Promise constructor function?
Eventually, you have to register a callback somewhere right? I mean you can't avoid having a callback that at-least resolves? In other words, we have to wrap it like this somewhere in the library correct?
At the lowest level? Yes, probably.
Depends on the context. If the value you want to resolve the value to is already available, then you'd use Promise.resolve(theValue). If you want to wait for multiple promises you'd use Promise.all(allThePromises). And of course every call to .then returns a new promise.
If you want to do anything with the value of a promise, then yes, you have to pass a callback to .then. There is no other way to get the value.
Having said all that, ES2017 introduced async functions which are basically syntactic sugar for promises. async functions always return a promise and you can use await to unwrap promises. Example:
async function get() {
return await Promise.resolve(42);
}
get().then(console.log);
At a low level, the functions that already support promises would be doing this right i.e constructing a promise using Promise and returning it back?
The eventual goal is that even the low-level APIs within Node.JS will be using the Promise syntax so there will be no need for callbacks. The PromiseJS community considers the constructor syntax to be a polyfill for this eventual behavior. Source.
In practice, this is obviously not the case. Browsers, Node.JS and other Javascript runtime environments make heavy use of callbacks, and so the Promise constructor is used.
Is there a better way to create a promise other than using the Promise constructor function?
Yes- but this depends on application.
Most obviously, if you are using an API which already returns a promise, you can use .then() and .catch() syntax to chain promises together.
There are also a number of libraries which extend the default Promise functionality and provide convenience methods for increased performance and readability. For instance, Bluebird (a common PromiseJS library) offers a way to evaluate the result of multiple promises created concurrently for faster overall runtime.
You can also construct Promises from other paradigms which simplify code- for instance Promise.Promisify which converts a callback function into a Promise one, or Observable.toPromise which converts Observables to promises.
Eventually, you have to register a callback somewhere right? I mean you can't avoid having a callback that at-least resolves? In other words, we have to wrap it like this somewhere in the library correct?
Similar to the answer to your first question, this is a symptom of code currently using the callback paradigm. If all libraries used Promises on a lower level, this wouldn't be the case.
At a low level, the functions that already support promises would be doing this right i.e constructing a promise using Promise and returning it back?
Yes. Maybe. At a low level, we don't know how functions that already support promises do construct those. Imagine what the Promise constructor itself uses to construct promises…
Is there a better way to create a promise other than using the Promise constructor function?
No, there's no other way accessible to JS. But as MDN notes, you should rarely ever need to do this yourself, and rather just use other functions that already create promises for you (which by themselves of course go through the Promise constructor somehow).
Eventually, you have to register a callback somewhere right? I mean you can't avoid having a callback that at-least resolves?
Not necessarily. At a low level, you could also register any arbitrary data structure that allows to resolve a promise, this does not necessarily need to be a callback function. (It is however indeed equivalent in power to registering the resolve callback function).
For example the native implementation of the fetch API (afaik the first native API that directly returns promises) will hardly create JS callback functions anywhere to resolve its promises.
I have taken this example from the Axios AJAX library but the same goes for Promises.all().
So far I have read you can use Promises.all() to check if all promises from an array of promises have been resolved.
This is really neat but what happens if you push a promise and it resolves before the next one has been pushed?
I am guessing with the overhead of my average AJAX call of at least 50ms the push will always happen before any ajax requests but to just say taken for this granted does not really feel right.
There are 2 solutions for this that I could think of:
Use a count to ensure both(in thise case) AJAX requests are in the
array.
Check for the actual function names being there.
How are others dealing with this or are most people simply satisfied with the hope of both AJAX requests being pushed before a single one can be resolved quick enough.
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
You do not need to worry about promises that resolve before they are passed to Promise.all: Promise.all will not be called before its (array) argument has been completely evaluated. Only when the array is ready, will Promise.all be called.
Whether or not any of those promises is already resolved, at the time Promise.all is called, is really not important. Promise.all will check which ones are in a resolved state, and will only call its then method when all of them have been fulfilled. It might even be that all the promises in the array are already fulfilled: no problem, as soon as Promise.all is executed, it will schedule the execution of the then method. Even the time during which those promises were already resolved does not matter. Even if they were resolved one hour ago, Promise.all will still do its job correctly.
Axios.all calls Promise.all which returns a single promise which resolves when all the promises in the iterable argument are resolved.
Axios.spread will be getting the resolved values from getUserAccount and getUserPermission.
Push is a sync operation, ajax is async. JS will always run all sync operations before running any async operation even if it has already finished. for example:
for (i=0;i<10000000;i++){
console.log('looping');
}
setTimeout(function(){
console.log('running async')
}, 0)
Although the timeout is set to 0, so it can immediately run, it will wait until the for loop is done and only then run the async operation.
So even if you push a promise and it immediately resolves, it will wait until the push is done and only then will react to the resolved promises.
It seems generally that creating deferred objects is now commonly discouraged in favor of using the ES6-style Promise constructor. Does there exist a situation where it would be necessary (or just better somehow) to use a deferred?
For example, on this page, the following example is given as justification for using a deferred:
function delay(ms) {
var deferred = Promise.pending();
setTimeout(function(){
deferred.resolve();
}, ms);
return deferred.promise;
}
However, this could be done just as well with the Promise constructor:
function delay(ms) {
return new Promise(function(resolve, reject){
setTimeout(function(){
resolve();
}, ms);
});
}
Does there exist a situation where it would be necessary (or just
better somehow) to use a deferred?
There is no such situation where a deferred is necessary. "Better" is a matter of opinion so I won't address that here.
There's a reason that the ES6 promise specification does not have a deferred object. You simply don't need one. Anything that people used to use a deferred object for can always be done another way that doesn't use a deferred object.
First off, the majority of uses fit very nicely into the promise constructor model. Secondly, any other cases that didn't fit quite so cleanly into that model can still be accomplished with the promise constructor or by starting with a resolved promise and chaining to it.
The main use case I've seen for a deferred is when you want to pass the ability to resolve() or reject() off to some other piece of code other than the code that created the promise. A Deferred made that very easy since you could just pass the deferred object and it had public methods for resolving or rejecting it. But, with a promise, you can also just pass the resolve and/or reject methods. Since they are bound to the specific object automatically, you can just pass the function references. And, in other cases, you can just let the other code create their own promise and resolve/reject it themselves and have that operation linked to yours rather than letting them resolve/reject your promise. Is all that quite as clean as passing a deferred object? Mostly a matter of opinion, but neither are very common use cases and all are something that can be accomplished without a separate deferred object.
And, as torazaburo points out, letting some external code resolve or reject your promise is a bit of an anti-pattern in its own right. You created the promise - you resolve/reject it. If you want to use external events to decide when to resolve/reject it, then have them notify you (via their own promise or callback) and you can resolve/reject your own promise. Or, have them create their own promise that you can use. That's really the desired model. Or, let them chain onto your promise so that the end result is gated by when their operation is done.
If one was used to coding with the deferred object (say with a jQuery deferred), it might take a little getting used to coding without it, but after a little while you just start to think differently and it starts to come natural to just use the promise constructor.
Once you promisify the sphere of async operations that you use in any given application, it's pretty rare that you ever even need to create your own promises any more because you are mostly just building off promises that the async functions you call are already creating or using flow control operations like Promise.all() which create uber promises for you.
This is the main point of anti-patterns. Use the promises that are already created for you rather than manually create more. Chain them. Return promises from .then() handlers to link async operations under logic control.
Of course, there are existing async operations that don't return promises for which somebody needs to create a promise, but that should be done in a promisify layer somewhere outside the purvey of your main coding logic and done only once for that operation and then code that calls the async operation should be able to just use the returned promises.
Native Promises don't have all the built-in methods that deferred has out of the box, for example the method to resolve the promise outside the scope of its constructor (very easy to implement with native Promise, though), and the ability to look at the status of a promise (which is hidden from regular JavaScript on native Promises, though it can be inspected in the dev tools).
The main reason to use deferred today would be backwards compatibility with code that depends on those extra methods. Hard to give a definitive answer to your question without stepping onto opinion territory.