Why to use Promise.resolve().then()? - javascript

While reading Angular's code for the directive RouterLinkActive, I found a curious usage of Promise.resolve:
private update(): void {
...
Promise.resolve().then(() => {
const hasActiveLinks = this.hasActiveLinks();
if (this.isActive !== hasActiveLinks) {
...
}
});
}
In which case is it useful to use Promise.resolve().then() instead of just executing the code that is inside of the then?
I've seen usages of setTimeout(() => defeferedFunction()) but first time seeing it with Promise.resolve.

The Angular framework does this a lot. It is used to prevent an error that you will see a lot (sorry don't know the actual wording of the error) something like 'The value was changed after it was checked'. This error happens if the Change Detector checked a value and then it was changed in the same cycle.
The Promise.resolve() pushes this code on to the microtask queue so it is executed after the JavaScript stack is empty. This prevents the error.

If you want your function to return a promise, but you only conditionally run code that involves promises, you may want to use this pattern.
Alternatively, just use async/await syntax.
So for example, if you have a function to fetch data, but the data may be cached, you can do something like
function fetchData() {
return Promise.resolve().then(function() {
if (cache && cache.data) return cache.data;
// if we're here, the data isn't cached.
return database.getData();
})
}
This way, it ALWAYS returns a promise that always resolves with the data you want, even when you're using the sychronous cache code

In simple terms, inside a then handler function:
A) When x is a value (number, string, etc):
return x is equivalent to return Promise.resolve(x)
throw x is equivalent to return Promise.reject(x)
B) When x is a Promise that is already settled (not pending anymore):
return x is equivalent to return Promise.resolve(x), if the Promise was already resolved.
return x is equivalent to return Promise.reject(x), if the Promise was already rejected.
C) When x is a Promise that is pending:
return x will return a pending Promise, and it will be evaluated on the subsequent then.

Related

Timing when converting promise to async await

I have the following block
return this
.configure(config => {
viewModel.configure(config, this);
return config;
})
.then(() => {
this.activate();
});
which, is equivalent to following block, suggested by vscode auto promise->async/await conversion:
await this.configure(config => {
viewModel.configure(config, this);
return config;
});
this.activate();
My question is are they actually the same in timing? In the promise example, shouldn't the second fn in then() 1 micro task away from the callback that accepts config?
Extra question for clarity: are the following equal in timing perspective:
Promise.all(tasks)
.then(() => {
othertasks.forEach(() => doStuff());
})
.then(() => {
prune();
});
and:
await Promise.all(tasks);
othertasks.forEach(() => doStuff();
await Promise.resolve();
prune();
EDIT: I should clarify, with regards to Andrea Giammarchi's answer, that my answer was purely and only related to the difference in number of ticks in-between the synchronously executed code, not whether the result of that code as-written is actually equivalent (obviously, the non-awaited promises would yield promises whereas the awaited promises would yield the values)
This would make more sense in the context that me and bigopon had a discussion in a github issue where he accepted VS Code's suggestion to remove a "redundant" .then from a promise chain in a piece of legacy code that happens to be sensitive to subtle timing issues.
I pointed out that this change would cause a particular method to be executed one tick earlier, and that the effects of that could potentially break complex apps relying on these (quirky) timings.
The discussion was then, whether this:
somePromise.then(() => {
...
}).then(() => {
doStuff();
})
Would have the same timings as this:
await somePromise;
doStuff();
To which my answer was: no, the doStuff() in the second snippet would execute one tick earlier.
When someone else suggested that await or .then would actually be executed synchronously if the passed-in promise was already resolved, that motivated me to write this answer and clarify why not.
I do realize that without this context, my answer can seem misleading, but again: it's just to point out the similarity in number of ticks.
Original answer
Example 1
For resolving a value, in plain terms, this:
await something
Is equivalent to this:
Promise.resolve(something).then()
They both result in a pending promise.
Example 2
For queueing a task, this:
await Promise.resolve();
doStuff();
Is equivalent to this:
Promise.resolve().then(() => {
doStuff();
})
In both cases, doStuff() happens on the next tick.
In order to determine whether a regular .then chain is equivalent to a series of awaits, you simply need to count .then and await. If the number of each is the same between two given pieces of code, then the amount of time/ticks/whatever passing between those pieces of code will be the same.
Example 3
Another example, this:
await Promise.resolve();
doStuff();
await Promise.resolve();
doStuff();
await Promise.resolve();
await Promise.resolve();
doStuff();
Is equivalent to this:
Promise.resolve()
.then(() => {
doStuff();
})
.then(() => {
doStuff();
})
.then(() => {})
.then(() => {
doStuff();
})
Note that the Promise.resolve() itself has no effect on the timings. It returns a resolved promise. It's the then() / await that turns it into a pending one.
So I respectfully disagree with amadan and I believe both your examples are equivalent.
What the spec says
If promise.[[PromiseState]] is "pending", then
a. Append fulfillReaction as the last element of the List that is promise.[[PromiseFulfillReactions]].
b. Append rejectReaction as the last element of the List that is promise.[[PromiseRejectReactions]].
Else if promise.[[PromiseState]] is "fulfilled", then
a. Let value be promise.[[PromiseResult]].
b. Perform EnqueueJob("PromiseJobs", PromiseReactionJob, « fulfillReaction, value »).
What this says is "if the promise is already pending, simply append the fulfillReaction of that pending promise, but of the promise is fulfilled, then enqueue a new job".
In other words, .then is guaranteed to return a pending promise regardless of whether the promise it is chained on was fulfilled or not.
I think there is quite some confusion in both what happened with VSCode, what you asked as question, and the kind of answer you received.
I'll try to clarify all points as I can, hoping I got the question right.
Let me start saying that ...
Those two blocks are not equivalent
The following piece of code:
this
.configure(config => {
viewModel.configure(config, this);
return config;
})
.then(value => `some ${value}`);
is "equivalent" only to this one:
await this
.configure(config => {
viewModel.configure(config, this);
return config;
})
.then(value => `some ${value}`);
That is because await has less priority than method chaining/then concatenation.
(async function (){
const then = await Promise.resolve(1).then(one => one + one);
console.log(then); // this is number 2
const something = await 123;
console.log(something); // this is number 123
}());
The reason you are rightly confused is that VSCode outsmarted your intent.
return this
.configure(config => {
viewModel.configure(config, this);
// configure() returns a Promise
// and config can be a Promise too
return config;
})
.then(() => {
// but you are not using here the config value
// or, if it was a promise, whatever value it resolved
// and you are also not returning any value
this.activate();
});
Since VSCode knows that configure is thenable, and its returned value could also be a Promise, which would imply the activate can happen only after config is eventually resolved, it also knows having an extra tick would make no sense because you don't need whatever config returned, either as value or promise, so that activate can be called right away.
Since you were also not returning any value in the last then, the whole return can be dropped.
// only async to wait for
await this.configure(config => {
viewModel.configure(config, this);
return config;
});
// with config either value or promise
// there's nothing else to wait for, so
// let's invoke activate without returning
// anything, producing is the same undefined result
this.activate();
To recap what happens inside that await:
(async function (){
const config = new Promise(res => setTimeout(res, 1000));
console.time('awaiting');
const value = await Promise.resolve(1).then(() => {
return config;
});
console.timeEnd('awaiting');
// awaiting: 1000.XXXms
}());
If you were by any chance using the returned value inside that last then, you would've seen that VSCode could not have dropped it, most-likely readdressed as const value = await ...; this.activate(value); which is also still OK.
To the previous comment stating:
For resolving a value, in plain terms, this:
await something
Is equivalent to this:
Promise.resolve(something).then()
They both result in a pending promise.
Not sure I read that wrong but that that felt to me quite a misleading statement.
const resolved = await anything means resolved is always a value, never a pending promise.
That's quite possibly the whole point of await: it won't stop awaiting until there is a value.
Example:
(async function (){
const something = Promise.resolve(Math.random());
// this logs the random number as typeof number
console.log(await something);
// this also logs the random number as typeof number
console.log(await Promise.resolve(something).then());
// while this is one is the only pending promise
console.log(Promise.resolve(something).then());
}());
The reason you eventually see Pending promise in console is that the AIIFE (Asynchronous Immediately Invoked Function Expression) is a promise itself and you can await it elsewhere.
You can see that returning a value or a pending promise will always produce the expected result.
(async function (){
// instant return, it's just fine
// return 123;
// return promise (unnecessary ticks added)
return Promise.resolve(123).then();
}()).then(console.log);
In both cases the 123 number is logged.
I hope it's clear now, specially for the OP, what happened in VSCode, and, specially, why happened.
Regards.

Javascript pattern for handling finished promise

I run into this every now and then:
return somethingThatReturnsAPromise()
.then((response) => {
soSomethingg(); // Eg; update the UI
return response;
});
Now I'm looking for something that is not expected to return anything and won't change the promise chain if I forget that:
return somethingThatReturnsAPromise()
.whatImLookingFor((response) => {
doSomething(); // Eg; update the UI
})
.then((response) => {
// and this one should still be able to access response
});
Maybe this goes against the idea of promises, but for me, it's a bit inconvenient since I can't pass arbitrary functions.
One idea is to compose a function:
const sideEffect = (callback) => {
return (response) => {
callback(response);
return response;
};
};
And I could use it as
return somethingThatReturnsAPromise()
.then(sideEffect(doSomething));
But I'd prefer something instead of then is there something like that?
Note: I'm working with Angular 1.x so I need something like for that.
I would assume that you're not really writing .then().then(), because you could collapse that into a single .then, but that your concern is really about returning the promise and having some external code add another then to the chain. In that case do this:
let p = somethingThatReturnsAPromise();
p.then(() => doSomething());
return p;
This allows the caller to attach additional thens to the original promise instead of chaining off of your .then, thereby receiving the original promise's value. This is called branching the promise chain.
Maybe this goes against the idea of promises
Slightly, promise chains are pipelines where then handlers transform things at each stage. But it's perfectly valid to want to pass through the value unchanged.
One idea is to compose a function:
Indeed the first thing that came to mind, and how I'd do it.
But I'd prefer something instead of then is there something like that?
There isn't. You could add it for your own projects (I wouldn't in a library) by adding it to Promise.prototype. Or you could give yourselve a Promise subclass and add it there.
With a Promise sublass you'd do something like:
return MyPromise.resolve(somethingThatReturnsAPromise())
.thenSide(soSomethingg); // Eg; update the UI
...where thenSide is your method that's then but passing the original value back unchanged, e.g.:
class MyPromise extends Promise {
thenSide(callback) {
this.then(callback);
return this;
}
}
or
class MyPromise extends Promise {
thenSide(callback) {
this.then(callback);
return MyPromise.resolve(this);
}
}
...depending on whether you're bothered about thenSide returning the same promise (since then always returns a new one).
As far as I know (I could well be wrong) the wrapper method for "pass-through" side-effects is an idiomatic way to do what you want.
Alternatively (if you need the same response in multiple places) you can break up the promise chain when you encounter a situation like this.

How do you prevent throwing exceptions in functions that return promises?

Functions that return promises should not throw exceptions but instead reject the promise, but what's the best practice for ensuring that this will always be the case? For example, even if I have an extremely simple class method like this...
foo(x) {
return this.promiseReturningApi.someMethod('bar', x);
}
...exceptions could be thrown if this.promiseReturningApi is undefined or someMethod is not a function (unless it's called after the first function in a promise chain).
To ensure that an exception is never thrown, is it necessary to always use a try-catch block like this:
foo(x) {
try {
return this.promiseReturningApi.someMethod('bar', x);
} catch (e) {
return Promise.reject(e);
}
}
(Or use something like Promise.resolve().then(() => this.promiseReturningApi.someMethod('bar', x)))
for any function that will return a promise, or how else would one ensure that exceptions are not thrown?
At the risk of committing the `explicit promise constructor anti-pattern, this would work:
foo(x) {
return new Promise(resolve => {
resolve(this.promiseReturningApi.someMethod('bar', x));
});
}
In the absence of any error being thrown, this will return the promise yielded by promiseReturningAPi as is. If an uncaught error is thrown anywhere, be it in the this.promiseReturningApi.someMethod line for whatever reason, or possibly even further down inside the call chain (such as inside promiseReturningApi, depending on how it is written), then the promise returned by foo will be rejected for that error reason. This is due to the specification of how thrown errors inside the "executor" (the function passed to new Promise) are handled--namely, they result in a rejected promise.
However, you should think long and hard about whether you really want to turn what is in essence an error in program logic (for example, a missing someMethod) into a rejected promise. You may find it is a better structure for your program to let "real" errors be errors, and bubble up and be reported as errors are, and reserve rejected promises for failed asynchronous operations such as a network request failing.
Here's what I think is the best practise
/**in provider**/
foo(x) {
return this.api.method(x); //Promise
}
/**in component**/
this.provider.foo('cat')
.then((res) => { console.log(res) }) //resolved
.catch((err) => { console.log(err) }) //rejected
In the function that returns the promise you could use promise.catch(...) like mentioned here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch instead of the try/catch wrapper. This would potentially allow you to just return this.promiseReturningApi.someMethod('bar', x);.

Return Promise result instead of Promise in Nodejs

Background
I am trying to learn promises, and I have a promise chain I want to improve on.
Problem
While learning how to chain promises, I fail to see why anyone would rather return a promise instead of returning it's value.
Take the following example, which uses promise chaining:
let myObj = new MyClass();
myObj.getInfo()
.then(result => writeOutput(FILE_NAME, result))
.then(console.log(FILE_NAME + " complete"))
.catch(error => console.error(error));
class MyClass{
getInfo() {
return new Promise(function(fulfil, reject) {
fulfill("I like bananas");
});
}
Here I have to chain 2 times. But if I were to directly return the result from the method getInfo() instead of returning a Promise I could potentially do something like the following:
let myObj = new MyClass();
let str = myObj.getInfo();
writeOutput(FILE_NAME, str)
.then(console.log(FILE_NAME + " complete"))
.catch(error => console.error(error));
Questions
So as you can see I am a little confused.
Given that getInfo() is in fact async, is it possible to achieve a similar code to the one in my second code sample?
If it were possible, would it be a good idea? How would you do it?
You can only return a value from some function if that value is immediately available when that function is called (on the same tick of the event loop). Remember that return is synchronous.
If it is not available right away then you can only return a promise (or you can use a callback but here you are specifically asking about promises).
For a more detailed explanation see this answer that I wrote some time ago to a question asking on how to return a result of an AJAX call from some function. I explained why you cannot return the value but you can return a promise:
jQuery: Return data after ajax call success
Here is another related answer - it got downvoted for some reason but I think it explains a similar issue that you're asking about her:
Return value in function from a promise block
I fail to see why anyone would rather return a promise instead of returning it's value.
Because you don't have the value yet.
Given that getInfo() is in fact async, is it possible to achieve a similar code to the one in my second code sample?
If it's asynchronous, it must return a promise.
A syntax that avoids then calls (but still uses and produces promises) is made possible by async/await:
async function writeInfo(FILE_NAME) {
const myObj = new MyClass();
try {
const str = await myObj.getInfo();
await writeOutput(FILE_NAME, str);
console.log(FILE_NAME + " complete");
} catch (error) {
console.error(error);
}
}
writeInfo("…");
The purpose of a promise is to say: "The data will be there sometime, just continue with your coding for now", so that your page won't get stuck for example. Basically you are using Promises for example to load something from the server.
Another way of looking at the first question ("why would anyone..."):
When you call .then or .catch on a promise, the pending promise returned from the call is appended to the end of an existing promise chain. (Allowing the "existing chain" might only contain one promise).
When you resolve (not "fulfill", you can't fulfill a promise with a promise) a promise with a promise, the promise used in resolution is inserted at the head of what remains of the promise chain.
The promise used in resolution must be fulfilled with a value before the next .then or .catch clause in the chain is called.

Execute other promise only if first one didn't returned good results

So I'm struggling with this a couple days and I have found a solution for this but I feel like this isn't the good one.
I currently have the following. I don't like it because I'm nesting Promises within promises. I don't know if this is fine but it doesn't look like it.
What I'm trying to accomplish with this code is first to check the cache database for a value if it is not there then I'll check the real database.
Any tips/tricks/pointers/comments on how to do this more elegantly?
var getData = function() {
var cancel = false
var cache = db.getFromCache(query)
.then((data) => {
// Check if data is up to date
if (uptodate) {
return Promise.resolve(data)
}
cancel = true
})
return cache
.then(() => {
if (cancel)
return db.getFromDatabase().then( //code)
}
}
ps: this code may or may not run I just made it quickly for this question. I can't past the real code here
When you're in a .then() handler, you can do anyone of the following:
Return a value - that value becomes the resolved value of the parent promise. So, there is no need to return Promise.resolve(value). You can just return value.
Return a promise - When you return a promise, it is chained to the parent promise and the parent promise will not resolve until this new promise resolves and the resolved value of this returned promise will become the resolved value of the parent promise.
Throw an Exception - If a .then() handler throws, that exception is automatically caught by the promise infrastructure and is turned into a rejection so throw err works similarly to return Promise.reject(err).
As such, when you're in your .then() handler, you can just check to see if the cache data is valid and, if so, just return it. Otherwise, return a new promise to get the data.
var getData = function() {
return db.getFromCache(query).then((data) => {
// Check if data is up to date
if (uptodate) {
// return cached data, will be resolved value of promise
return data;
} else {
// get data from db, return promise that will be chained
return db.getFromDatabase();
}
})
}
getData().then(...)
Your code is way more complicated than need be:
You don't need Promise.resolve(). You can just return the value.
You don't need the cancel variable at all. You can do all your work inside the first .then() handler.
You don't need the second .then() handler.
Promises support chaining, which means that a promise can return another promise, and this one can return another one, and so on.
According to MDN:
You can pass a lambda (anonymous function) to then and if it returns
a promise, an equivalent Promise will be exposed to the subsequent
then in the method chain.
When a value is simply returned from within a then lambda, it will
effectively return Promise.resolve().
This mean that in the then block, you can check if the data is up to date in the cache. If the data is fresh return it, and the value will be wrapped in a new promise. If the data is stale, you can return the call getFromDatabase(), which returns promise:
const getData = (query) => db.getFromCache(query)
.then((data) => isUpToDate(data) ? data : db.getFromDatabase(query));
getData().then(/** code **/);
Returning from a promise wraps the returned data with a new promise, so you can manipulate the data, and return it, and it will be wrapped by a promise automatically:
db.getFromDatabase().then((data) => data.map(/** some code **/)); // result will be wrapped in a promise.

Categories