(Related but not quite the same: JS Promises: Fulfill vs Resolve)
I've been trying to wrap my head around Javascript promises, and I'm struggling with the basic notions of resolve and resolved, vs. fulfill and fulfilled. I have read several introductions, such as Jake Archibald's, as well as browsing some relevant specs.
In States and Fates (not quite an official spec, but referenced as an authoritative document written by one of the spec authors), fulfilled is a state while resolved is a "fate" (whatever that is -- but they're clearly distinct):
Promises have three possible mutually exclusive states: fulfilled,
rejected, and pending.
A promise is fulfilled if promise.then(f) will call f "as soon as
possible."
and
A promise is resolved if trying to resolve or reject it has no effect,
i.e. the promise has been "locked in" to either follow another
promise, or has been fulfilled or rejected
In particular, resolved encompasses both fulfilled and rejected (and locked in). The "opposite" (or directly corresponding function) to reject is fulfill, not resolve; resolve includes reject as one of its possibilities.
Yet the spec refers to the first argument to the then() method (or its corresponding abstract concepts) using both fulfill and resolve:
25.4: A promise p is fulfilled if p.then(f, r) will immediately enqueue a Job to call the function f.
25.4.1.1: [[Resolve]] A function object The function that is used to resolve the given promise object.
25.4.1.3: Set the [[Promise]] internal slot of resolve to promise.
Set the [[AlreadyResolved]] internal slot of resolve to alreadyResolved. [Then immediately after, reject is used in an exactly corresponding way.]
25.4.5.3: Promise.prototype.then ( onFulfilled , onRejected )
Maybe one of the most crucial is
25.4.4.5 Promise.resolve ( x )
which MDN describes as follows:
The Promise.resolve(value) method returns a Promise object that is resolved with the given value. If the value is a thenable (i.e. has a "then" method), the returned promise will "follow" that thenable, adopting its eventual state; otherwise the returned promise will be fulfilled with the value.
There is no mention of the Promise.resolve() method having the potential to reject. Furthermore, there is a Promise.reject() method, but no Promise.fulfill() method. So here, the counterpart to reject is resolve, instead of fulfill.
Granted, there might be no guaranteed correlation between the "fate" term resolved, and the method (or verb) resolve. But it would be (is?) really misleading and confusing to have resolve() put a promise into a specifically fulfilled state, when the terms fulfilled and resolved have carefully been defined to have distinct meanings.
Is that what has happened here... the right hand didn't know what the left hand was doing, and we ended up with the documents that are supposed to be the guiding lights of the movement using terms in inconsistent ways? Or is there something I'm missing, and resolve is actually a more appropriate term than fulfill for what the resolve() method does?
I don't mean to come across as critical of the document authors. I understand how, with a widely-distributed group, over history, it's difficult to get all the terms to be used consistently across all documents. My purpose here, in digging into the terminology and phrasing of these documents, is to understand the terms accurately -- which includes knowing the limits of how precisely terms like fulfill and resolve can really be distinguished. Jake Archibald admits that he sometimes gets the terms mixed up. That's a very helpful admission to noobs like me who are trying to make sense of terminology! Thank you, Jake, for being vulnerable. :-) My purpose in asking this question is to find out, which definitions or usages of those terms are reliable? Or should I conclude that resolve is sometimes used specifically to mean fulfill, and sometimes fulfill/reject/lock in, even in the most authoritative documents?
I'm struggling with the basic notions of resolve and resolved, vs. fulfill and fulfilled.
Have a look at What is the correct terminology for javascript promises. Yes, they're sometimes mixed up, but let's try to ignore that.
Yet the spec refers to the first argument to the then() method (or its corresponding abstract concepts) using both fulfill and resolve. Did we end up with the documents that are supposed to be the guiding lights of the movement using terms in inconsistent ways?
No, there is no inconsistency here, the terms in the spec are accurate.
resolve is not the opposite of reject, and the onFulfilled callback does not exactly correspond to resolving.
Or is there something I'm missing, and resolve is actually a more appropriate term than fulfill for what the resolve() method does?
Yes. The problem is that you can resolve with a promise (or in general, a thenable, i.e. any object with a then method), and instead of fulfilling with that object the resolved promise will try to fulfill with the thenable's result. Or when the thenable has (will have) an error, it will reject with that as the reason.
There is no mention [in MDN] of the Promise.resolve() method having the potential to reject.
Actually there is: "the returned promise will "follow" that thenable, adopting its eventual state". If that eventual state is the error state, then the returned promise will reject as well.
There is a Promise.reject() method, but no Promise.fulfill() method. So here, the counterpart to reject is resolve, instead of fulfill.
Yes, ES6 promises are lacking a Promise.fulfill method. This is a bit inconsistent and confusing at first, but was done for a good reason: It prevents you from having a promise that is fulfilled with another promise. You can only resolve ES6 promises, and when you pass in a promise it will take its result not the promise object by itself.
This is pretty useful, but can be pretty inconvenient as well. There are libraries that do it better and allow this case, they're so-called "algebraic promises" (which you can reason about a lot better). Creed is a good example of that, and its Promise.of method is a real counterpart to Promise.reject.
Let's add emphasis somewhere else in one of your quotes :
A promise is resolved if trying to resolve or reject it has no effect, i.e. the promise has been "locked in" to either follow another promise, or has been fulfilled or rejected
reject() will change your Promise and all the prior-chained ones status to rejected.
While resolve() will lock your current Promise, but only the fulfillment of the resolving callbacks will set its status to fulfilled. As long as everything is not finished, your Promise will still be 'pending'.
e.g If you do chain your Promise, and that during the chain, an error is thrown, then the state of your Promise is set to 'rejected'.
var p = new Promise((resolve, reject) => {
resolve(); // here p is resolved, we can't call resolve() or reject() anymore
}).then((e) => { // chain it
return new Promise((resolve, reject) => {
console.log(p); // 'pending' because it's still chained
reject('whatever') // this one throws an error and breaks the chain
})
}).then(() => console.log('passed')); // won't happen
setTimeout(() => console.log(p, "please check in your browser's console"), 1000); // rejected
So even if you are resolve()-ing the current operation of your Promise, you can't know what will happen after, so you can't know if at the end it will be fulfilled or rejected.
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.)
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
The "executor" function I am referring to:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise#parameters
And the then() method:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
Seems like there is some overlap between their meaning or purpose.
In both cases, we often name their parameters "resolve" and "reject" - (Edit: this is obviously false, I mixed things up somehow).
I would almost think the functions passed to then() are somehow passed on to the executor.
But that doesn't seem to be the case:
The documentation points out, that the functions passed to the executor are automatically generated:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise#parameters
At the time when the constructor generates the new Promise object, it also generates a corresponding pair of functions for resolutionFunc and rejectionFunc; these are "tethered" to the Promise object.
And we can also settle the promise without ever calling the then() method.
To summarize:
I am thinking that in case of the executor, the resolve and reject functions are functions generated by the constructor, but we control calling them to settle the promise with a result.
The resolve and reject functions we write and pass to then() are automatically called with the same result as before, but after the promise is settled.
Is this correct?
There is no real connection at all between the two as they are completely different things for completely different purposes.
The executor function is what determines when the promise eventually resolves or rejects. The two arguments are functions you call when the promise should resolve (with an optional value) or should reject (with a reason).
The one or two arguments passed to .then() are listeners that get called when the promise changes state, either becomes resolved or becomes rejected. They don't determine when the promise gets resolved or rejected, but are instead listeners to when that happens.
Promises (at the 10,000' level) are just a monitoring system for asynchronous event completion or error. The executor determines when the promise completes and the .then() or .catch() handlers allow you to add listeners so you get notified when the promise becomes fulfilled or rejected.
I am thinking that in case of the executor, that the resolve and reject functions are functions generated by the constructor, but we control calling them to settle the promise with a result.
Yes, that is correct - they are generated in the constructor. You control when/if they are called.
The resolve and reject functions we write and pass to then() are automatically called with the same result as before, but after the promise is settled.
Think of these as onFulfilled and onRejected listener functions. Yes, the appropriate one is called when the promise is settled.
A few other points. You can have a promise with an executor with no .then() at all. For example, you might only have a .catch() because all you want to know about this operation is if it had an error or not. So, even without any onFulfilled listener, the promise executor works just fine (because it has nothing to do with the listeners).
And, you can have multiple .then() listeners on the same promise if you have multiple parts of your code interested in the outcome of this promise. You can even add a .then() listener after the promise is already fulfilled or rejected and it will get called on the next cycle of the event loop. I mention these just to further confirm that there's no correlation between .then() listeners and the resolve/reject handlers you get from the promise executor.
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.
I am looking to standardize the use of Q promises in my team's codebase. Are there any good jscs extensions (or other linters) to help enforce style when it comes to promises?
We would like our promises to follow this form:
promise()
.then()
.catch()
.done();
And would like a linter to catch any .then() in our code that is missing a .catch()
Advice for other stylistic tips when it comes to promises is welcome too.
#Jeff that approach looks as total overkill. Neither of this functions must be followed with any. Each of them has different purpose:
Use then(mapSuccess, mapFail) when you want to process resolved value and you need a result promise that will resolve with value returned by callback.
Technically it's a mapping of value into other value which will be resolved by other promise. You may think of it similarly as of array's map with which you map input array into other which is result of some transformation function.
catch(mapFail) is purely alias for then(null, mapFail), So just use it when you want to then but you have no need to pass mapSuccess callback.
done(onSuccess, onFail) use simply, when all you want to do is to process resolved value (no need for mapping to other promise). done will also assure that all eventual errors are naturally exposed (then and catch as they're mappers, swallow errors into promise results).
I can imagine only one rule, which can be added for linter (and it's assuming that you use library which doesn't log swallowed exceptions). It's to warn about then() or catch() usages when their results are being ignored (they should be followed by done(..) or passed to other entity for processing).