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.
Related
What is a difference between:
Promise.all([1,2]) //Promise {<fulfilled>: Array(2)}
and
let p1 = Promise.all([1,2]);
console.log(p1) //PromiseĀ {<pending>}
Why does the console show a different result?
Preface: I'm assuming you pasted both lines of your second example (let p1 = Promise.all([1,2]); and console.log(p1)) at the same time; that's how I can make Chrome's console do what you've described.
Why does the console show a different result?
The important thing first up: It's a console thing. It doesn't matter to how you write your own code consuming promises. The console is doing something "magic" your code can't do: accessing the state of a promise object directly and synchronously. That lets it build and display those one-liner snapshot strings (Promise {<fulfilled>: Array(2)} and Promise{<pending>}). But your JavaScript code can't access the state of a promise directly, it can only attach fulfillment and rejection handlers to it and get that information later, asynchronously. So there's no need to worry about the difference in what you're seeing and if you like, you can just stop reading now. :-)
What's happening with the console then?
Let's look at Promise.all([1, 2]) first:
Promise.all loops through each element in your array
It wraps each element in a Promise via Promise.resolve
It attaches fulfillment and rejection handlers to those promises
It rejects its promise if any of the input promises rejects, or if all the promises are fulfilled, it fulfills its promise with an array of the results (in order)
Because the promise from Promise.all can't be settled until the Promise.all code receives notification that the promises wrapped around 1 and 2 have settled, the promise from Promise.all will always start out pending, never fulfilled. But since the promises wrapped around 1 and 2 are fulfilled, they'll do that notification as soon as possible: When the current task (the current job in the job queue) finishes. Which means the promise from Promise.all goes from being pending to being fulfilled as soon as the code you've typed/pasted into the console is done running, but not while it's running. That, combined with the "magic" thing the console is doing, explains the difference. Let's see how:
In the first example, you've just put Promise.all([1, 2]) in the console, and had the console show you the result when your code is done. That means the console sees the promise from Promise.all after your code is done running, and apparently, after the Promise.all code has received the notification that the promises around 1 and 2 have been fulfilled. So when the console sees the promise object, it sees that it's already fulfilled, so it displays a one-liner string with Promise {<fulfilled>: Array(2)}.
In the second example, though, you've pasted let p1 = Promise.all([1,2]); and console.log(p1) together, so they get processed together. That means that when console.log(p1) runs, the promise from Promise.all is still pending, because it hasn't had a chance (yet) to receive notification that the 1 and 2 promises have been fulfilled. So the console displays the one-liner string Promise {<pending>}. Then your code ends, and the promise gets fulfilled almost immediately afterward.
So it's interesting behavior, but it doesn't really matter. In both cases, the promise from Promise.all is initially pending and then is fulfilled as soon as it can be. The only reason you see the difference is that the console can "magically" see the state of a promise directly and synchronously, which our code can't.
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.)
Does Promise.all() run in sequential or parallel in Javascript?
For Example:
const promises = [promise1(), promise2(), promise3()]
Promise.all(promises)
.then(data => {
// whatever
});
Does promise1() execute and resolve before moving onto promise2() or does promise1(), promise2(), and promise 3() all run in parallel at the same time? I would assume like Node, Javascript in the browser to be single threaded thus they don't run in parallel?
Javascript is a single threaded application. However, asynchronous calls allow you to not be blocked by that call. This is particularly useful when making REST API calls. For example your promise1() can make a REST API call and before it waits for the results, another REST API call can be made from promise2(). This pseudo-parallelism is thus achieved by not waiting on API servers to do the tasks and fire multiple such calls to either same or different API endpoints in parallel. This allows your code to continue executing that parts that are not dependent on resolution of the promises.
So yes, promise1(), promise2() and promise3() can be said to be running in parallel in that respect. And there is a chance that promise2() gets resolved before promise1() and so on. The function Promise.all() waits for all the promises provided to it to fulfill or at least one of them to fail.
Learn more about Javascript event loops in this video by Jake Archibald.
Promise.all does not make your promises run in parallel.
Promise.all does not make your promises run at all.
What Promise.all does, is just waiting for all the promises to complete.
The line of code that actually executes things is this one:
const promises = [promise1(), promise2(), promise3()]
Assuming that your promises make HTTP calls:
promise1() is executed -> 1 HTTP call going on
promise2() is executed -> 2 HTTP calls going on
promise3() is executed -> 3 HTTP calls going on
then after a while, in undetermined order, these promises complete.
These 3 HTTP calls could complete in the same time, but in your code, you will have 3 sequential completition. For this reason, you can use Promise.all to assure that your callback is executed only when all of your promises complete.
Remember that there's a limit on the amount of parallel HTTP connections you can have inyour environment, look at this: https://stackoverflow.com/a/985704/7327715
Inside of a then() function, if I didn't return a promise but calling the function directly.
doSomething().then(function () {
doSomethingElse(); //I know I should return doSomethingElse()
}).then(finalHandler);
I know doSomethingElse & finalHandler will run in parallel then instead of running sequentially. But I am still not sure why is that exactly?
doSomething
|-----------------|
doSomethingElse(undefined)
|------------------|
finalHandler(undefined)
|------------------|
When you run code in a .then() handler, you get the following design choices:
1. Return nothing. That leaves the return value undefined and that is a signal to the parent promise that there is no additional asynchronous operation to wait for here so the promise chain can continue running the next steps in the chain.
2. Return a promise. This tells the parent promise that you want to "insert" a promise into the chain and the following .then() handlers should not be called until this promise is resolved. The chain will essentially wait for this promise. If this new promise is ultimately resolved, the next .then() handler will get called. If this new promise is ultimately rejected, the next .catch() handler will get called.
3. Throw an exception. This tells the parent promise that the operation in the .then() handler failed and the parent promise chain immediately becomes rejected and the next .catch() handler will get called.
So, in your case, if doSomethingElse() is an asynchronous operation and you don't return a promise that is connected with that asynchronous operation, then you've just "branched" your promise chain into two separate chains. The main parent chain will continue calling the next .then() handler because you returned nothing. Meanwhile, your doSomethingElse() function is essentially its own parallel promise chain. It could even have it's own .then() handlers as in:
doSomethingElse().then(...).then(...).catch(...)
That would just be a completely separate promise chain that would have no connection at all to the other promise chain except for the timing of when this other promise chain was started. Once it starts, it runs independently from the other chain. This is typically referred to as "branching" in promise terminology. You branch into a new chain. The two run separate form one another. If both branches use asynchronous operations (which they presumably do), those asynchronous operations would be interleaved and both in flight at the same time. The timing of when they both finished would be completely indeterminate (since they have no programmatic relationship in their timing).
Branching to a completely independent promise chain like this is usually a programming error and some promise implementations may report a likely programming error in the console. The reason this is usually an error is there is no way for anyone outside this code to have any way to monitor or catch errors in the branched and independent promise. And promises without error handling are bad. They eat errors silently.
There are certain cases where you legitimately don't change your program behavior if an error happens. Often times when you're closing a file at the end of a long sequence or even just trying to close files after errors have occurred, you just want to make your best efforts to close the file and you don't really have anything more useful to do if the close fails (except perhaps log the failure) so there's no particular reason to try to propagate back that type of failure. But, this should only be done in a very thoughtful way. 99.9999% of the time, errors should be propagated back to the caller and creating a new branched and independent promise chain like this does not propagate its errors back anywhere so it's usually not the right coding strategy.
The function does not need to return a Promise. If nothing was explicitly returned, by default undefined is returned. Functions in Javascript work like that. See the example
function doSomething() {
}
console.log(doSomething());
When you return a Promise from the function in the then chains, then will work only if the returned Promise is resolved. If an exception was occurred, the catch function will work if the last exists.
So actually your code is like
doSomething().then(function () {
doSomethingElse();
return undefined;
}).then(finalHandler); `undefined` is passed into the `finalHandler` function
What about the parallel, they will work not in parallel, but sequentially if the code is then(...).then(...). These then work sequentially. But if your doSomethingElse also returns a Promise, it will have its own sequence of chain. It's flow is independent from the doSomething flow.
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.