I have a single page app in angular. There is a global available state available through a context service.
This context service has a get and set method. The get method is a promise since the context is not yet set up once the page is loaded but is gotten through an API.
Once the context is set-up the promise is resolved and I can access my context through the get method.
However how can I deal with the set method. It is possible to change the context with the set method. But since the promise is already resolved the get method will return the old context.
Is it possible to 'substitute' the data the promise returns on a then call after it has been resolved?
Thanks!
The get method is a promise...
That doesn't make much sense. I'm assuming it returns a promise.
However how can I deal with the set method. It is possible to change the context with the set method. But since the promise is allready resolved the get method will return the old context.
Code shouldn't keep and reuse the old promise like that. E.g., you're suggesting:
var p = object.getThePromise();
p.then(function(value) {
// ...use the value...
});
// later - this is the bit that's wrong
p.then(function(value) {
// ...use the value...
});
It shouldn't do that. It should go back to getThePromise if it wants the value again later (if it wants a new value).
Is it possible to 'substitute' the data the promise returns on a then call after it has been resolved?
No. An important part of the promise contract is that a promise is only ever settled once, and the settled value does not change.
It's hard to say without seeing your API, but if the API gives the impression you can call the "get" and then reuse the resulting promise, it would be best to change the API to make it no longer give that impression. But again, without API specifics, it's hard to say whether that's the case or suggest a change.
Related
I have been working with Promises in Javascript lately but I have difficulty trying to understand what exactly happens here.
A Google Maps script url is being set with a callback of initMap. So when Google Maps finishes loading this callback will fire.
In turn it calls resolveFunc(). resolvefunc() is set within the promise but I don't get this part:
resolveFunc = resolve;
What is the use of setting it equal to the resolve function?
<script>
var resolveFunc = null;
var promise = new Promise(function(resolve, reject) {
resolveFunc = resolve;
});
promise.then(function() {
console.log('loaded');
});
function initMap() {
resolveFunc();
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?callback=initMap" async defer></script>
The only reason I can see for this construction is that you want to be able to access a Promise that resolves when the google maps api is loaded.
A new promise object is created.
The resolve function of that promise is assigned out of scope in to a window variable resolveFunc
A .then is assigned to the promise that issues a console log when the promise resolves.
The assigned resolve function is called when the initMap function is run by the external gmaps script.
The console.log inside the .then will fire.
I can't see any need for this that running code inside the initMap without the promise construction could also achieve.
I've never seen this construction before.
I can only think of one reason for it: to allow some sort of modularization. The loading of the Google Maps URL will trigger the resolution of that promise. In the meantime, other modules may have attached themselves with .then to this promise, to be run when this script has been loaded.
That global promise variable is really ugly, but it would serve for this purpose.
Note: I mean 'module' in its generic sense: some reasonably independent, cohesive chunk of code.
The idea behind this pattern is that the script, that is loaded with:
src="https://maps.googleapis.com/maps/api/js?callback=initMap"
..., should be able to resolve the promise. For that the script needs to call the resolve function that the promise constructor provides. But since resolve is local to that constructor, the authors have used a solution where they assign that function to a global variable, which will be available to the loaded script.
That script will certainly look at the callback argument present in the url, and extract the value following it. It will be assigned to some variable, let's say callback, and then they will trigger the resolution with:
window[callback]();
which will be synonym for resolveFunc, which in turn is synonym for resolve.
That script could not have done that with a call to resolve:
resolve();
... as that would be an unknown variable.
This approach is known as Deferred pattern which was notably implemented in jQuery. It is prone to be antipattern but is possibly justified here because it's a reasonable way to move from Google Maps API callback to a promise.
promise resolution is deferred until resolveFunc callback is called. promise is global and can be used inside the application. In case it's not used, this can be considered antipattern for the reasons mentioned above:
It is easy to fall into this when you don't really understand promises and think of them as glorified event emitters or callback utility.
It could be just:
<script>
function initMap() {
console.log('loaded');
}
</script>
In the OpenUI5 code-base I came across this snippet:
// Wait until everything is rendered (parent height!) before reading/updating sizes.
// Use a promise to make sure
// to be executed before timeouts may be executed.
Promise.resolve().then(this._updateTableSizes.bind(this, true));
It looks like the native Promise function is being used, with no argument being passed to it's resolve function which takes an:
Argument to be resolved by this Promise. Can also be a Promise or a
thenable to resolve.
So, since it looks like the promise would simply immediately resolve and invoke then's callback, perhaps the intent is similar to:
var self = this;
setTimeout(function() {
self._updateTableSizes.bind(self, true)
}, 0);
...basically, freeing the JavaScript run-time event-loop to finish other things (like rendering) and then come right back to the callback?
My question is:
Is this a common pattern? Best-practice? Are there any advantages/disadvantages to either approach?
Yes, Promise.resolve() does immediately fulfill with the undefined value that you implicitly passed in. The callback is still executed asynchronously - quite like in the setTimeout snippet you posted.
However, as the comment in the code explains, the intent is not just to execute the callback asynchronously:
Use a promise to make sure to be executed before timeouts may be executed.
Promise callbacks do run before timeouts or other events, and these subtle timing differences are sometimes important. Given that choice of the task loop is usually not important, No this is not a common pattern; but it is a valid pattern that does exactly what you need when you need it.
I noticed the technique in this polyfill: https://github.com/wicg/inert (with comment)
const newButton = document.createElement('button');
const inertContainer = document.querySelector('[inert]');
inertContainer.appendChild(newButton);
// Wait for the next microtask to allow mutation observers to react to the DOM change
Promise.resolve().then(() => {
expect(isUnfocusable(newButton)).to.equal(true);
});
I need to write some modules that load data one time and then provide an interface to that data. I'd like to load the data asynchronously. My application already uses promises. Is providing a promise as the result of requiring a module a valid pattern/idiom?
Example Module:
var DB = require('promise-based-db-module');
module.exports =
DB.fetch('foo')
.then(function(foo){
return {
getId: function(){return foo.id;},
getName: function(){return foo.name;}
};
});
Example Usage:
require('./myPromiseModule')
.then(function(dataInterface){
// Use the data
});
UPDATE:
I've used this for a while now and it works great. One thing I've learned, and it's hinted at in the accepted answer, is that it is good to cache the promise itself, and whenever you want to access the data use then. The first time the data is accessed, the code will wait until the promise is resolved. Subsequent usage of then will return the data immediately. e.g.
var cachedPromise = require('./myPromiseModule');
cachedPromise.then(function(dataInterface){
// Use the data
});
...
cachedPromise.then(function(dataInterface){
// Use the data again somewhere else.
});
This seems like a perfectly good interface for a module who's job is to do a one-time fetch of some data.
The data is obtained async so a promise makes sense for that. The goal is to fetch the data just once and then let all places this module gets used just have access to that original data. A promise works great for that too because it's a one-shot device that remembers its state.
Personally, I'm not sure why you need the getId() and getName() methods when you could just offer direct access to the properties, but either can work.
A downside to this interface is that there is no means of requesting a fresh copy of the data (newly loaded from the DB).
I have a pretty silly problem. Consider the following:
vm.feed = getFeed().then(function(data) {return data;});
getFeed() returns a $q deferred promise (I am on angular) that resolves successfully.
My goal is to set vm.feed equal to the data value returned by the successful callback. As it is right now, the code simply assigns vm.feed equal to the $promise object returned by getFeed().
I know I could simply do: vm.feed = data inside the resolved function but I want to understand why this code does not work as it is.
PD: the promise resolves correctly and even after it has been resolved vm.feed keeps being equal to the Promise, and not data. I copy the console.log of vm.feed after +10 seconds have elapsed:
Promise {$$state: Object} $$state: Objectstatus:1 value: Object
That value property inside the Promise object contains the actual solution of the promise that I want to assign to vm.feed (e.i. data).
thank you!
This could be updated to ES6 with arrow functions and look like:
getFeed().then(data => (vm.feed = data));
If you wish to use the async function, you could also do like that:
async function myFunction(){
vm.feed = await getFeed();
// do whatever you need with vm.feed below
}
Edit: added parenthesis to be eslint correct (as commented by Max Waterman)
You are going to get whatever then() returns. But since you are reading this, the following may help you:
Your statement does nothing more than ask the interpreter to assign the value returned from then() to the vm.feed variable. then() returns you a Promise (as you can see here: https://github.com/angular/angular.js/blob/ce77c25b067b7b74d90de23bfb4aac6a27abb9d1/src/ng/q.js#L288). You could picture this by seeing that the Promise (a simple object) is being pulled out of the function and getting assigned to vm.feed. This happens as soon as the interpreter executes that line.
Since your successful callback does not run when you call then() but only when your promise gets resolved (at a later time, asynchronously) it would be impossible for then() to return its value for the caller. This is the default way Javascript works. This was the exact reason Promises were introduced, so you could ask the interpreter to push the value to you, in the form of a callback.
Though on a future version that is being worked on for JavaScript (ES2016) a couple keywords will be introduced to work pretty much as you are expecting right now. The good news is you can start writing code like this today through transpilation from ES2016 to the current widely supported version (ES5).
A nice introduction to the topic is available at: https://www.youtube.com/watch?v=lil4YCCXRYc
To use it right now you can transpile your code through Babel: https://babeljs.io/docs/usage/experimental/ (by running with --stage 1).
You can also see some examples here: https://github.com/lukehoban/ecmascript-asyncawait.
The then() method returns a Promise. It takes two arguments, both are callback functions for the success and failure cases of the Promise. the promise object itself doesn't give you the resolved data directly, the interface of this object only provides the data via callbacks supplied. So, you have to do this like this:
getFeed().then(function(data) { vm.feed = data;});
The then() function returns the promise with a resolved value of the previous then() callback, allowing you the pass the value to subsequent callbacks:
promiseB = promiseA.then(function(result) {
return result + 1;
});
// promiseB will be resolved immediately after promiseA is resolved
// and its value will be the result of promiseA incremented by 1
You could provide your function with the object and its attribute. Next, do what you need to do inside the function. Finally, assign the value returned in the promise to the right place in your object. Here's an example:
let myFunction = function (vm, feed) {
getFeed().then( data => {
vm[feed] = data
})
}
myFunction(vm, "feed")
You can also write a self-invoking function if you want.
This is one "trick" you can do since your out of an async function so can't use await keywork
Do what you want to do with vm.feed inside a setTimeout
vm.feed = getFeed().then(function(data) {return data;});
setTimeout(() => {
// do you stuf here
// after the time you promise will be revolved or rejected
// if you need some of the values in here immediately out of settimeout
// might occur an error if promise wore not yet resolved or rejected
console.log("vm.feed",vm.feed);
}, 100);
I am working on an admin app with AngularJS. The app fetches its data from the server using $resource. I end up with data objects containing '$promise' property to determine when the data has been fetched.
Everything's fine.
Now, this admin app can also create new objects. Those new objects are managed by the same controllers than the one that usually come from '$resource'.
So now I have 2 kind of objects:
Objects with $promise property. I should use $promise.then() before manipulating them with full data
Plain objects. They don't have a $promise property, their value is accessible instantly
I would like to reduce code and to have a single use-case, not having to check if object data is resolved, or if it is not a promise.
Is there any problem in building my 'plain' objects by adding them a '$promise' property which is already resolved to themselves? That way, I would always use 'myObject.$promise.then()'.
Is there any common pattern to handle this situation? I could not find any 'standard' method to create this kind of objects with Angular.
You could use $q.when if unsure whether the object has a promise or not.
(obj.$promise ? obj.$promise || $q.when(objResult)).then(function(result){
//handle success case.
//Incase of object not having the $promise property result will be object itself
})
if the resultant property does not have a promise this will resolve with promise.
Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. This is useful when you are dealing with an object that might or might not be a promise, or if the promise comes from a source that can't be trusted.
You do not need to always create a promise and attach it on the data that is being transferred, instead you could make your methods returns promise thus help you implement promise pattern and abstract out the promise logic on your service itself. Example:-
function getCurrentUserData(userId){
var defered = $q.defer();
...
//Check in my cache if this is already there, then get it from cache and resolve it
defered.resolve(objectFromCache);
//my else condition
//It is not in cache so let me make the call.
$http.get('myurl').then(function(result){
//Do validation with data and if it doesnot match then reject it
defered.reject(reason);
//Else Do something with the data put it into the cache, mapping logic etc..
defered.resolve(dto);
}).catch(function(error){
//do something with error and then reject
defered.reject(reasonDerived);
});
return defered.promise;
}
Here is a simplified and less explicit version (Credit: Benjamin Gruenbaum):
var cached = null;
function getCurrentUserData(userId){
return cached = cached || $http.get('myurl').then(function(result){
if(isNotValid(result)) return $q.reject(reason); // alternatively `throw`
return transformToDto(result);
}, function(response){
if(checkforsomethingonstatusandreject)
return $q.reject('Errored Out')
//do some actions to notify the error scenarios and return default actions
return someDefaults; });
}
You can of course return $q.reject(derivedReason) here rather than returning the reason and transform it based on further checks, the idea is caching the promise rather than the value. This also has the advantage of not making multiple http requests if the method is called before it returns once.
Now you could always do:-
getCurrentUserData(userid).then(function(user){
}).catch(function(error){
});
Promises can be chained through as well. So you could also do this:-
return $resource('myresource').$promise.then(function(result){
//Do some mapping and return mapped data
return mappedResult;
});
A+ promises should offer a static method:
Promise.resolve(val);
...which generates a pre-resolved promise. You should return one of these if your promises library offers this. Great. I do this frequently to avoid interface duplication.