Javascript Promise Question, why does Chain not work [duplicate] - javascript

This question already has answers here:
Why does the Promise constructor require a function that calls 'resolve' when complete, but 'then' does not - it returns a value instead?
(5 answers)
Closed 1 year ago.
So this works
new Promise(res => {
console.log("1")
res(res);
}).then(res => {
console.log("2")
}).then(res => {
console.log("3")
})
But if I exclude the res(res), it will not chain. But it chains fine afterwards without any additional res().
Why is that, why does the first block matter if it has that res() and the subsequent blocks don't need it in order to chain onwards ?
Thanks.

It chains just fine without the call to res() in that the succeeding .then() handlers are chained onto the original promise.
But, the original promise will never be resolved if you don't execute res(someValue) so therefore, it will never call your .then() handlers. At some point, you have to resolve the original promise at the start of the chain if you want any of the .then() handlers to get called. Until you call res(someValue), you have a promise chain where the head of the chain is sitting in the pending state waiting to be resolved or rejected so it can then run the rest of the chain.
But if I exclude the res(res), it will not chain. But it chains fine afterwards without any additional res().
The original promise has to be resolved before the first .then() handler will get called. From then on, just returning from a .then() handler resolves the next promise in the chain - you no longer have to manually resolve it.
From within a .then() handler, you can trigger different outcomes three different ways:
Return from the .then() handler. This resolves that chained promise and triggers its .then() handlers to get called. If you return a value, that value becomes the new resolved value of this part of the chain. If you don't return a value or there's an implicit return, then the resolved value is undefined.
Throw an exception from within the .then() handler. This will be automatically caught by the promise infrastructure and will cause this promise in the promise chain to reject - trigger any .catch() handler to get called.
Return a promise from the .then() handler. This causes the current promise in the chain to track this new promise and it will resolve or reject based on what the newly returned promise does.
Also, a given promise can only be resolved or rejected once. It's a one-way state machine. Once it has been resolved or rejected, it's state is fixed and can not be changed again. So, you would only ever call res(someValue) once. Attempting to call it more than once just does nothing as the promise has already been changed to the fulfilled state and can no longer be changed.
FYI, the three states of a promise are pending, fulfilled and rejected. When you call res(someValue), you change the state of the original promise in your chain from pending to fulfilled and the internals of the promise will then call its .then() handlers on the next tick.
Something that people often don't realize is that each call to .then() creates and returns a new promise that is linked to the earlier promise chain. So, the code in your question actually creates 3 promises, one from new Promise() and two from the two calls to .then(). These subsequent promises from calls to .then() get resolved or rejected only when the parent promise gets resolved or rejected and the .then() or .catch() handler gets called and then there's a return value from that .then() or .catch() handler. Those return values from those handlers determine what happens next in the chain.

Related

why callback in promise.then dont't return anything will make the state of promise returned by .then 'resolved'? [duplicate]

I'm getting confused with the different terminology thrown around. From my understanding, a promise can be:
fulfilled
rejected
pending
settled
resolved
defer
Does resolve mean settled? or does it mean its fulfilled? and what the heck is defer?
Terminology can be hard.
Let's take from the Promises/A+ specification and the respective ES6 section that there are 3 states:
pending - the promise does not have taken a value yet, it's future is still uncertain.
fulfilled - the promise successfully got a result value "assigned"
rejected - the promise is given a reason why no result could be acquired, typically an error.
The term settled is a hyperonym for fulfilled and rejected, meaning either - the opposite of pending.
The dynamic verbs fulfill and reject describe changing the state from pending into either the fulfilled or rejected. These transitions are called fulfillment or rejection of the promise.
Those were easy. Now, resolve is a different beast. It sometimes is used synonymous to "fulfill", but it would better be understood as settling the promise's fate to either fulfilled or rejected. The resolution (seldom: settlement) of a promise means that it leaves the pending state. But not even that is accurate - the problem is the recursive nature of the Promise Resolution Procedure:
resolving a promise with a "plain" value means fulfilling it
resolving a promise with a promise (or thenable) means adopting its state:
resolving with a fulfilled promise is a fulfillment
resolving with a rejected promise is a rejection
resolving with a pending promise means waiting for its resolution
Yes, if a promise is resolved it might not even be known whether it's going to be fulfilled or rejected. But it means the fate is no longer undetermined, as it's bound to the promise that we resolved with (notice that you can resolve a promise only once).
Ignoring this special case, a resolved promise usually means a settled promise.
Or, to cite the ECMAScript 6 Specification:
A promise is resolved if it is settled or if it has been “locked in” to match the state of another promise. Attempting to resolve or reject a resolved promise has no effect. A promise is unresolved if it is not resolved. An unresolved promise is always in the pending state. A resolved promise may be pending, fulfilled or rejected.
and what the heck is defer?
Deferring a result means that you return an (asynchronous) promise for the result, and not the result directly (synchronously). And also return a deferred rejection instead of throwing synchronously.
Notice that "defer" is also used in some libraries (Q) as the method name to construct a Deferred object - see this answer on The differences between Deferred, Promise and Future for a good explanation.
Oh, and never trust variable names: defer might as well be an abbreviated "deferredObject".
The three promise states are listed in section 2.1 of the Promises/A+ specification.
From the specification:
So here are each of the terms you asked about:
Pending is the initial promise state. The operation represented by the promise has not yet been filfilled or rejected.
Fulfilled is another of the three promise states. It means that the promise has been resolved and now has its resolved value. The operation represented by the promise has been completed successfully.
Rejected is another of the three promise states. It means that the promise has been rejected and now has its rejected reason. The operation represented by the promise failed to obtain a value and thus has a reason for failing to do so (typically an error code or error object, but it can be anything).
Settled is a term that means the promise is either fulfilled or rejected (e.g. it's not pending any more), but it is not a separate state just a descriptive term to indicate it is no longer pending.
Resolved is a term that is often used to mean the same as fulfilled, but the two are not exactly the same. A promise can be resolved with a value which leads to fulfillment or it can be resolved with a rejected promise (which leads to rejection of this promise) or it can be resolved with a pending promise (which means it will now be waiting on the eventual state of some other promise).
It's hard to say exactly what you mean by defer. Promises are often classified as deferred objects in that they are an object that represents an action and result that is deferred to the future (it will occur in the future). In some promises implementations, there are actually two types of objects, a deferred object and a promise object. The deferred object is a superset of the promise object. Both can observe when the action is resolved or rejected with .then() handlers. But, only the deferred object can actually change the state to resolved or rejected.
In jQuery, you can create a deferred object with $.Deferred(). In other implementations such as ES6 promises, you just have promise objects with a constructor callback that has reject and resolve functions. The world is presumably moving toward what ES6 will have.
jQuery example that uses a deferred object:
function delay(t) {
var defer = $.Deferred();
setTimeout(function() {
defer.resolve();
}, t);
return defer.promise()
}
delay(200).then(function() {
// run my delayed function now
doMyThing();
});
ES6 promise example:
function delay(t) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
resolve();
}, t);
});
}
delay(200).then(function() {
// run my delayed function now
doMyThing();
});
Domenic Denicola's "States and Fates" is a good, pithy summary.
States:
a promise is fulfilled if promise.then(f) will call f "as soon as possible"
a promise is rejected if promise.then(undefined, r) will call r "as soon as possible"
a promise is pending if it is neither fulfilled nor rejected.
Fates:
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
a promise is unresolved if it is not resolved, i.e. if trying to resolve or reject it will have an impact on the promise.
Follow the link for details 'relating states and fates'.

Order of operations after promises

I'm pretty new to JavaScript (using Node.js) and still learning. I try to wrap around my head with promises and I got into this situation where I don't understand if there is a difference between this code:
promiseFunc().then(() => {
anotherPromiseFunc() // I dont need .then() here, just want to save some data to DB
});
doSmthElse()
and this code:
promiseFunc().then(async () => {
await anotherPromiseFunc() // I dont need .then() here, just want to save some data to DB
});
doSmthElse()
If I don't use .then() in the first case, does it mean there is a possibility that doSmthElse() will be executed before anotherPromiseFunc() is executed? So in order to prevent that, I have to add async/await? Or all code in .then() block is being waited to execute anyway before doing something else?
Note 1: I don't want to chain those promises, because there is more code in my case, but I just simplified it here.
Note 2: I don't use catch because if error will rip through I will catch it later.
If I don't use .then() in the first case, does it mean there is a possibility that doSmthElse() will be executed before AnotherPromise() is executed?
doSmthElse() is guaranteed to be executed before anything in the fulfillment handler¹ is executed. Promise fulfillment and rejection handlers are always invoked asynchronously. That's true whether you declare the handler function using async or not.
For example:
console.log("before");
Promise.resolve(42)
.then(result => {
console.log("within", result);
});
console.log("after");
The output of that is:
before
after
within 42
So in order to prevent that, I have to add async/await?
Where you've added async/await in your example doesn't change when doSmthElse() is executed.
If you want doSmthElse() to wait until the promise has been fulfilled, move it into the fulfillment handler.¹
If your code were inside an async function, you could use await instead, like this:
// Inside an `async` function
await promiseFunc().then(() => {
anotherPromiseFunc();
});
doSmthElse()
That would do this:
Call promiseFunc() and get the promise it returns
Hook up a fulfillment handler to that promise via then, returning a new promise; that new promise is the operand to await
Wait for the promise from #1 to settle
When it does, your fulfillment handler is executed and runs anothterPromiseFunc() but doesn't wait for the promise it returns to settle (because, as you said, you're not returning that promise from the fulfillment handler).
At this point, the promise from #2 is fulfilled because your fulfillment handler has (effectively) returned undefined, which isn't a thenable (loosely, a promise), so that value (undefined) is used to fulfill the promise from #2.
Since the promise from #2 has been fulfilled, await is satisfied and doSmthElse() is executed.
¹ the function you pass then as its first argument
I assume that Promise() just stands for some function call returning a Promise:
You could say that .then registers an event listener that runs as soon as the promise settles => the task is finished. It still runs asynchronously So in your example doSmthElse will still run even if the promise hasn't been settled (so if the promise doesn't settle immediately doSmthElse will be called before the function inside .then)
To let your code run "in order". You would have to use await to ensure that doSmthElse is called after the promise settled or you could put doSmthElse into the .then block.

Promise Immutability in Javascript

I know that Promises in JS are Immutable.
According to: What does it mean for promises to be immutable and their guaranteed value?
A promise is a one-way latch. Once it is resolved with a value or
rejected with a reason, its state and value/reason can never change.
So, no matter how many times you do .then() on the same promise, you
will always get the same result.
So I am confused about this image from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
It looks like after the Promise is either fulfilled or rejected (resolved) it returns a Promise that is (pending). Why is that?
Every call to .then() or .catch() returns a new promise. That promise will be resolved or rejected depending upon what happens in the .then() or .catch() handler that it derives from when that handler is called. The promise at the start of the chain is indeed immutable, but once someone else calls .then() or .catch() on it, they get a new promise back from each of those calls whose state is determined by what happens inside the code that runs in the .then() or .catch() handler.
When you get that new promise from .then() or .catch() it will always be in the pending state because the code inside the .then() or .catch() has not yet been called (the original promise has not yet called them). Those statements just register handlers on the original promise.
When those handlers are called, they will determine the state of the new promise. If they return a plain value, that new promise will be resolved with that value. If they throw, that new promise will be rejected. If they return a promise, then that new promise will follow the eventual state of that new promise that was returned form the .then() or .catch() handler and it will resolve or reject as that returned promise does.

Is it possible to catch errors between chained promises?

So I am wondering if this works?
S3.getObject()
.promise()
.then()
.catch() // catch error from the first then() statement
.then()
.catch() // catch error from the second then() statement
or do I need to place all 'catches' in the end? Can I have multiple catch then? Will they be fired in the order of the 'then' statements throwing errors?
It depends of your actual goals.
As a matter of fact, .then() method takes two parameters:
onFullfilled: Callback to be invoked when the promise is fulfilled.
onRejected: Callback to be invoked when the promise is rejected.
In fact, .catch(fn) is just a shorthand for .then(null, fn).
Both .then() and .catch() each return a new promise which resolves to its return value. In other words:
A resolved promise of that value if it isn't a promise.
The actual return value if it is already a promise (that will be fulfilled or rejected).
A rejected promise if the return value is a rejected promise (as previous point says) or any error is thrown.
The main reason behind the use of .then(onFullfill).catch(onReject) pattern instead of .then(onFullfill, onReject) is that, in the former (which is equivalent to .then(onFullfill).then(null, onReject)), we are chaining the onReject callback to the promise returned by first .then() instead of directly to the original promise.
The consequence of this is that if en error is thrown inside the onFullfill callback (or it returns a promise which happen to resolve to a rejected state), it will be catched by the chained .catch() too.
So, answering to your question, when you do something like:
P.then(...)
.then(...)
.then(...)
.catch(...)
;
You are chaining promises "supposing" all will go fine "and only check at the end". That is: Whenever any step fails, all subsequent .then()s are bypassed up to the next (in this case the last) .catch().
On the other hand, if you insert more .catch()s in between, you would be able to intercept rejected promises earlier and, if appropriate, solve whatever were going on and turn it into a resolved state again in order to resume the chain.

What's the purpose of the promise object that gets returned by the .then() method?

I have read on multiple websites, that the .then() method from the promise.prototype returns a promise. Unfortunately, no source describes the reason behind this.
The then() method returns a Promise. It takes up to two arguments: callback functions for the success and failure cases of the Promise. - developer.mozilla.com
Why/When would someone need this returned promise object, how is this promise object related to the original object.
Thanks a lot for helping.
A promise is executed asynchronously, you never know when the then() will be executed.
And a promise can return a promise, this allows you to chain asynchronous events handling in singles lines of code.
Example code given by Mozilla:
doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);
It avoids the "pyramid of doom" :
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
There are three main aspects to the fact that .then() returns a promise.
The first is that you can chain operations like this:
a().then(b).then(c).then(d)
Because .then() returns a new promise, the following .then() handlers will not be executed until that new promise resolves. If b and c are synchronous, then that new promise will resolve when they return and the chain will continue when first b is done and then when c is done.
The second is that the new promise can be influenced by what the .then() handler returns. This allows b, c and d to be asynchronous operations that, themselves returns promises and the chain will be sequenced appropriately. So, imagine that b and c both return promises themselves.
First you get a() returning a promise. When that resolves resolves, its .then() handler gets called. That will then run b. If b() is also an async operation and it returns a new promise, then the promise that a.then(b) returns that all the other .then() handlers are linked to will NOT be resolved until that new promise that b returned is resolved. This allows a .then() handler to insert a new asynchronous item into the chain. This is a very important aspect of chaining promises. .then() handlers can insert them own asynchronous operations into the chain and they can even do it conditionally based on prior results or current state.
If a().then(b) just returned the same promise that a() returns, then all the subsequent .then() handlers would not be able to "wait" for the promise that b() returns because they would have been linked to the a() promise and it has already resolved. It is the returning of this new promise that allows the function inside the .then() handler to influence the subsequent chain because that new promise is influenced by what the .then() handler returns.
The third aspect is that the return value of the .then() handler can influence the resolved value of the new promise and that is what is passed to the next .then() handler in the chain. If a().then(b) just returned the same promise that a() returns, then all the subsequent .then() handlers would just see the same resolved value from a() because that resolved value was already set when a() resolved which is before a().then() has called its .then() handler. These subsequent .then() handlers wouldn't be able to inherit a new resolved value from then code inside the .then() handler.
Let's look at a specific scenario. I'll use a delay method as a simple example of a function that returns a promise that resolves in the future.
function delay(t, val) {
return new Promise(resolve => {
setTimeout(() => resolve(val), t);
});
}
Then, define several different async functions:
function a(val) {
return delay(100, val + 1);
}
function b(val) {
return delay(50, val + 10);
}
function c(val) {
return val * 100;
}
Now, put them all in a chain:
a(100).then(b).then(c).then(val => {
console.log("all done: ", val);
});
Here's what happens step by step:
a(100) is called. This calls delay (which sets a timer) and returns a promise which I will call a1_promise just for purposes of describing things here.
Then, because we're doing a(100).then(b), we take the return value from a(100) which is the a1_promise and call a1_promise.then(b). That stores away the b function as a .then() handler function to be called sometime in the future when a1_promise is resolved (not right now). That then returns a new promise which I will call a2_promise.
Then, because we're doing a(100).then(b).then(c), we take the return value from a(100).then(b) which is the a2_promise and call a2_promise.then(c). That stores away the c function as a .then() handler function to be called sometime in the future when a2_promise is resolved (not right now). That then returns a new promise which I will call a3_promise.
Then, because we're doing a(100).then(b).then(c).then(...), we take the return value from a(100).then(b),then(c) which is the a3_promise and call a3_promise.then(c). That stores away our last anonymous function as a .then() handler function to be called sometime in the future when a3_promise is resolved (not right now). That then returns a new promise which I will call a4_promise (which nobody uses).
Now we're done with synchronous execution. Note that a().then(b).then(c).then(...) was all executed synchronously. All three .then() methods have already been called on all the different promises. But, because NONE of the promises created here are yet resolved, none of the .then() handlers have actually been called yet. They've all just been stored away to be called in the future when the promises are resolved.
Now some time passes and the timer created inside of a() fires and resolves a1_promise. That then triggers a1_promise to call any .then() handlers it has and pass it the resolved value of the a1_promise which in this case will be 100 + 1 or 101. Since there is just one .then() handler on the a1_promise and it is the b() function, it will call b(101) now. Executing that will just return a new promise which b() created and returned. We will call that new promise b_promise. Inside the a1_promise() it knows that it created the a2_promise() when a1_promise.then() was previously called so it knows that when it executes that stored .then() handler and that .then() handler executes and returns a new promise, then it holds off on resolving the a2_promise that it created until thatb_promiseis resolved. In this way, you can see that further execution of the chain is now controlled by theb_promise, thus the code executing inb()and the promise is returned are inserted into thea().then().then().then()chain holding off future.then()handlers until theb_promise` is resolved.
Now some more time passes and the timer created inside of b() fires and resolves the b1_promise with a newly modified value of 101 + 10 which is 111. This tells the a2_promise that it can now resolve with that value.
The a2_promise can then call it's .then() handler and can execute c(111) which again just like in step 6 returns c_promise which is not yet resolved.
Some time passes and c_promise resolves with a value of 111 * 100 which is11,100. That tells thea3_promise` that it can now resolve with that value.
The a3_promise can then call it's .then() handler which is our arrow function at the end of the chain and we get a console.log() showing 11000 as the final value.
This is the good part of a Promisse.
You can chain a lot of methods where which one depends from a method result (in this case Promisse.resolve) above, for example. Like this

Categories

Resources