This question already has answers here:
How do I access previous promise results in a .then() chain?
(17 answers)
Closed 8 years ago.
What is the correct pattern, when coding with promises, to access data coming from long before in a chain of promises?
For example:
do_A.then(do_B).then(do_C).then(do_D).then(do_E_WithTheDataComingFrom_A_And_C_OnlyWhen_D_IsSuccesfullyCompleted)
My current solution: passing along a single JSON structure through the chain, and let each step populate it.
Any opinion about that?
I don't think there's one "correct" pattern for this. Your solution sounds neat, however, it's a bit tightly coupled. It may work great for your situation, but I see a few problems with it as a general pattern:
Participating steps need to agree on the structure of the collector object.
Every step needs to participate in at least the forwarding of the object, which can get tedious if the chain is long and the need for previous data occurs sporadically. This is also inflexible to insertion of steps not written by you (chaining doesn't always happen linearly).
Said differently: Unless do_A|B|C|D and do_E are under your control, you'll need to wrap them with boilerplate to store off your collector object in a closure and convert to and from the functions' natural inputs and results, since the functions wont be "in on" your pattern.
On the other hand, if the functions are in on it, then the data-dependencies between the steps have effectively become hidden inside the functions. This may look clean, but it could become a maintenance problem. For example: if this is a team project, then someone might think they can re-order your steps, absent any no clue in the call-pattern what inputs do_E requires.
I would suggest a more straightforward approach using closures:
var a, c;
do_A()
.then(function(result) { a = result; return do_B(); })
.then(do_C)
.then(function(result) { c = result; return do_D(); })
.then(function() {
return do_E_WithTheDataComingFrom_A_And_C_OnlyWhen_D_Succeeds(a, c);
})
.catch(failed);
There's no collector object to define; do_A|B|C|D and do_E can be generic functions without knowledge of any pattern; there's no boilerplate unless returned data is relied on (do_B and do_D); and the data-dependencies (a and c) are explicit.
Yes, this is the correct way to chain state with actions.
Chaining .then statements is very common and is usually our building block when piping things around. It's at the very core of promises.
What you're doing is both correct and idiomatic.
For the curious spirit let's show this.
In order to verify this - we can check the promises specification.
We want to verify that:
It chains
In the case of a rejection, it doesn't call the handler in the chained then
It rejects the next promise returned from then with the same reason
It executes in sequence passing return value.
Let's verify these in order, using the specification - in particular .then:
1. It chains
7.1 then must return a promise [3.3].
Great, let's verify that it also chains on fullfillment
If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure >[[Resolve]](promise2, x).
Great, so we know that when our promise resolves or rejects then our then handler is called with the appropriate parameter. So .then(do_A).then(do_B) will always work assuming do_A resolves.
2. In the case of a rejection, it doesn't call the handler in the chained then
7.iv. If onRejected is not a function and promise1 is rejected, promise2 must be rejected with the same reason.
Great, so it rejects and calls onRejected if it's there, if it doesn't it chains.
3. It rejects the next promise returned from then with the same reason
We just covered that in 2.
4. It executes in sequence passing return value.
That is again
If either onFulfilled or onRejected returns a value x, run the Promise Resolution Procedure [[Resolve]](promise2, x).
So, if you set onFulfilled it'll run the resolution process. The resolution process itself dictates:
The promise resolution procedure is an abstract operation taking as input a promise and a value, which we denote as [[Resolve]](promise, x). If x is a thenable, it attempts to make promise adopt the state of x, under the assumption that x behaves at least somewhat like a promise. Otherwise, it fulfills promise with the value x.
If/when resolvePromise is called with a value y, run [[Resolve]](promise, y).
Where y is the return value of x.
Great! so it works.
Related
The documentation for promises states that .catch()returns a promise. Does this mean that if you return a promise in your code it will get wrapped in another promise from the .catch?
i.e.
const x = Promise.reject().catch(() => Promise.resolve("test"));
Will the above promise that resolves with the value of "test" be wrapped in another promise from the .catch? To my understanding this is what happens in async functions; they wrap whatever the result is in side of a promise "under the hood". Which could then result in double promises, is that also happening here?
If the promise in the variable x above was put into an existing array and passed to Promise.all(), what would constitute as being resolved? Would the catch block and the inner function have to complete before Promise.all resolves? If so, why is this the case? Why would Promise.all not resolve as soon as the first Promise.reject() is executed? How would it know to wait?
Yes and no ... there are multiple promises involved, but they are chained, not wrapped in each other, or in the final promise "takes on" (the actual phrase is "adopts") the value of the Promise.resolve("test") in your case, so, what you get is a single promise that settles (in this case, resolves) to the value "test"
The 5 Answers are:
yes, it is resolved,
yes, because x is the final promise returned by that expression
because x is the final promise returned by that expression, none of the other Promises are "visible" to the Promise.all,
because x is the final promise returned by that expression, the other Promises are not "visible" to x
that's how promise chains work, there's no waiting, it's just promise chaining at work
You may find The Promise Resolution Procedure - 2.3.2 helps in understanding the inner workings of Promises - 2.3.2 specifically deals with returning a Promise inside .then ... the rest of that resolution procedure is also illuminating.
I’m working on an Angular 6 application and I’ve been told the following is an anti-pattern:
await someFunction().then(result => {
console.log(result);
});
I realize that it is pointless to await a promise chain. If someFunction() returns a promise, you don’t need a promise chain if you’re awaiting it. You can do this:
const result = await someFunction();
console.log(result);
But I’m being told awaiting a promise chain can cause bugs, or that it will break things in my code. If the first code snippet above does the same thing as the second snippet, what does it matter which one is used. What dangers does the first snippet introduce that the second one doesn’t?
I’m being told awaiting a promise chain will break things in my code.
Not necessarily, your two code snippets do indeed work the same (as long as someFunction() really returns a promise).
What does it matter which one is used. What dangers does the first snippet introduce that the second one doesn’t?
It's harder to understand and maintain, it's confusing to mix different styles. Confusion leads to bugs.
Consider that you would need to add another promise call at the location of the console.log() call, or even a conditional return from the function. Can you use await in the callback like elsewhere in the function, do you need to return the result from the then callback, is it even possible to return from the outer function? All these questions don't even come up in the first snippet. And while they can be easily answered for your toy example, it might not be as easy in real code with more complex and nested control flow.
So you should prefer the more concise and clean one. Stick to await for consistency, avoid then in async functions1.
1: Of course, there's always an exception to the rule. I would say that it's cleaner to use promise chaining for error handling in some cases where you would use catch or the second then callback.
Under the hood, async/await is just promises.
That is, when you have some code that looks like:
const result = await myAsyncFunction();
console.log(result):
That's exactly the same as writing:
myAsyncFunction().then(data => {
const result = data;
console.log(result);
});
The reason then - that you shouldn't mix async/await and .then chains - is because it's confusing.
It's better to just pick one style, and stick to it.
And while you're picking one - you might as well pick async/await - it's more understandable.
If your then code returned a promise instead of calling console.log, your first example would await, but your second example would not.
When you use async/await, you will catch your rejects in try/catch blocks. Your code will be less nested and clearer.
Using then often results in more nesting, and harder to read code.
You can await anything, whether or not it returns a promise. Sometimes this future-proofs calling a method that may one day become async or just return a promise without declaring async.
The downsides are complexity, performance, and compatibility, all of which pale in comparison to the gains.
I find that if you rely on a function's return value after calling it, and it is or may eventually become asynchronous, decorate calling your functions with await to your heart's delight, whether or not it is current async or returns a promise.
Within my Node.JS application I have written a function (findByReference) that goes to a database and asynchronously yields a fetched database row. I have written this function using Promises. Additionally, I have written an implementation of the Maybe monad and want my findByReference function to yield an instance of Maybe.
My code looks like the below:
findByReference(r)
.then(raw => raw ? Just(raw) : Nothing())
.then(row => {
(row instanceof Maybe) === true;
});
Without going into what Just and Nothing mean, the implication of this (because of how I've written Maybe) is that the row variable in the above code has a function on it called "then". To cut a long story short, it appears that Javascript is getting confused and is for some reason automatically calling MY "then" and instead of passing the Maybe is actually passing to the callback whatever MY "then" returns as the value of row. This is obviously leading to all manner of weird behaviour. If I simply remove the "then" function from my object then it all works as expected.
I am aware that if a Promise.then returns another Promise, then execution will pause until that promise is resolved. I have been unable to find any official documentation to back this up, but is it the case that this decision is simply based on the existence of a "then" function (the closest I have found is this https://developers.google.com/web/fundamentals/primers/promises which refers to the return value as "something Promise-like"). If this is the case, it would be my understanding that "then" as a function name is basically a reserved word in Javascript? I have seen other implementations of Maybe (such as this one https://www.npmjs.com/package/data.maybe) that use the word "chain" for a similar thing - I wondered if this is why?
Can anyone shed any light on if my deduction here is correct and if so is there any workaround I can use other than renaming my function?
FYI the only other SO question I've found that touches this problem is this one - Resolve promise with an object with a "then" function - but since that is angular-specific I don't believe this is a duplication.
Thanks in advance!
...the row variable in the above code has a function on it called "then". To cut a long story short, it appears that Javascript is getting confused and is for some reason automatically calling MY "then"...
It's not confused. :-) This is the definition of how promises work. JavaScript's promises work according to the Promises/A+ specification, which uses this terminology:
1.1 “promise” is an object or function with a then method whose behavior conforms to this specification.
1.2 “thenable” is an object or function that defines a then method.
If you have an object passing through a promise chain that's a thenable but not a promise, it's incompatible with promises.
So yes, in a sense, the then property of objects passing through promise chains is "reserved" by the Promises/A+ spec. You'll need to wrap your raw value in an object that doesn't have a then (and then unwrap it later). Or if you can, rename then in your design to remove the conflict.
I have an asynchronous function that returns a promise. The operation should only be performed once. I want all callers of that function to get back the same Promise, but I don't want .catch()es of one caller to affect another caller. Can I clone a promise, or implement this in another way?
but I don't want .catch()es of one caller to affect another caller.
They never1 do (unless you've chained the callbacks, which you don't).
I want all callers of that function to get back the same Promise
Just do it. Promises are immutable values2.
Can I clone a promise?
If you really need3 a distinct object that will follow the original promise (fulfill when it fulfills or reject when it rejects), you can use the then method without arguments:
var clone = promise.then();
console.assert(clone !== promise);
1: Assuming you use a proper promise library. I think I can remember a case of a library (old jQuery?) where then callback results changed the state of the promise.
2: In their resolving behaviour, at least. Every promise is still just an object of course.
3: You don't. You really should not. I'm just answering the title question, but you should stop doing weird stuff.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
Having started learning Ember, I get confused whenever there is a reference to Promise Object. I am aware of objects e.g. instance of class X or a JSON object. For instance, when somebody says that method m returns a JSON object then I know to expect key-value pairs. How do I relate this understanding to promise objects? Is a promise object a value (could be array, string, number, JSON object, etc.)?
What should I expect if a method returns a promise object?
A promise is a new Object type of EcmaScript 6 (ES6), for which there are numerous polyfills and libaries (i.e. implementations for ES5 JavaScript engines), and allows (among other benefits) to get out of the infamous callback hell, and to write and read asynchronous code easily.
A promise can have one (and only one) of these three status:
pending
fulfilled
rejected
If the promise is rejected or fulfilled, it also has a settled status.
Basically, it is an object that has a then property (amongst others), which is a function that takes at least one function as a parameter, and can take two: the first one will be invoked if the promise returns a fulfilled status, and the second one will be invoked if the promise returns a rejected status
The then function returns itself another promise, so promises are chainable.
Promise objects are rather more complicated than what I just wrote here, but it was just to give you a start.
BTW, you may have used a promise-like object (note the -like suffix) if you use jQuery : $.ajax() returns a promise-like object (those are called thenables) that has a done (and a then) property which is a function that accepts a function as the parameter that seems like a fulfilled function (which normally takes only one argument). Promise objects also may have a done function property (not standardised, AFAIK, but almost all the polyfills and the libraries implement it), which acts like a then function, only it does not return a promise (hence the name: if you are done with the promise, then use done(), but if you need to do somehting with the result of the promise, use then()).
e.g.: you may have seen or written something like this:
$.ajax({url: '/path/to/html/piece'})
.done(function(data) {
$('whateverSelector').html(data);
});
But what jQuery calls promises, even if they are thenables, does not fulfill the promise spec.
Promise object is what it's name says it is - a promise. In frontend frameworks like Ember and Angular promise is an object returned by an asynchronous call. This call instead of blocking the whole system returns you a promise, which will eventually contain the data the asynchronous call returns.
Promises have an event called resolve, which is triggered when the response come. That's why promise objects have method usually called then. You can use it like this (after Ember.js docs):
var promise = fetchTheAnswer();
promise.then(fulfill, reject);
function fulfill(answer) {
console.log("The answer is " + answer);
}
function reject(reason) {
console.log("Couldn't get the answer! Reason: " + reason);
}
When your request is fulfilled the promise will call the method fulfill with answer (response) as an argument, and when the request is rejected (ie. when the resource doesn't exist) it will call method reject with reason (error) as an argument.
Such objects are similar to objects in other OO languages like java - they contain both data and methods you can call on them.