Angular $promise without using defer - javascript

Everything on the net about promises these days says don't use defer when using promises except docs/examples on angular.js.
What is the proper way to return the value of an angular.js $promise without using defer?
Here's what I currently have:
function foo() {
var deferred = $q.defer();
userService.findProgramsByUser({personId: personId}).$promise
.then(function (programs) {
deferred.resolve(programs);
});
return deferred.promise;
}
If i was using the node Q library I would do this:
function foo() {
return Q.promise(function(resolve, reject) {
userService.findProgramsByUser({personId: personId})
.then(function (programs) {
resolve(programs);
});
}
}
How can I do something similar with an angular promise without having to use defer?

Since userService.findProgramsByUser() already has a promise property, just return that
function foo() {
return userService.findProgramsByUser({personId: personId}).$promise
.then(function (programs) {
return programs;
}, function(){
// handle errors
});
}

I really don't understand why it's bad to use defer. In most cases my services looks like:
var defers = {}; //somewhere in the service root.
getData: function(id){
var defKey = 'getData' + id;
if(defers.hasOwnProperty(defKey)) return defers[defKey].promise;
//here is a good spot to return something from custom cache
defers[defKey] = $q.defer();
serverApiRequest('/api/...').then(function(response){
defers[defKey].resolve(response.data);
delete defers[defKey];
});
return defers[defKey].promise;
}
Deferred promises are awesome. With such approach we can be 100% sure that server will be requested only one time and every parallel requests would be served.

Related

Javascript Promises on Angular Controller

I don't familiar with JavaScript Promises function.
Currently I have this code on my Angular Controller
$http.get('pages/about.html').then(function(response) {
var raw_html = response.data;
$scope.aboutHTML = raw_html.replace(/</g,"<");
});
I want to re-write the code so I could do something like this
$scope.indexHTML = getHTML('pages/index.html');
$scope.aboutHTML = getHTML('pages/about.html');
...
with function like this
function getHTML(url){
$http.get(url).then(function(response) {
var raw_html = response.data;
return = raw_html.replace(/</g,"<");
});
}
How to write the the code properly for the function above?
[Update #1]
Temporary Solution by #charlietfl
function getHTML(url){
// return the promise
return $http.get(url).then(function(response) {
var raw_html = response.data.replace(/</g,"<");
return raw_html;
});
}
getHTML('pages/index.html').then(function(raw_html){
$scope.indexHTML = raw_html;
});
I wanna to write this function to reduce the manual work, with this way I still need to write down $scope.{page} for each page, so anyone know better way?
[Update #2]
Solution by #joeytwiddle
function getHTML(url){
// return the promise
return $http.get(url).then(function(response) {
var raw_html = response.data.replace(/</g,"<");
return raw_html;
});
}
getHTML('pages/index.html').then(function(raw_html){
$scope.indexHTML = raw_html;
});
There is no way to just return the result, because the result will not be available until some time in the future. #asynchronous
You can only handle the result using a callback function.
If you want to minimize the work from outside, I would suggest something like this:
getHTMLAndStore('pages/index.html', $scope, 'indexHTML');
getHTMLAndStore('pages/about.html', $scope, 'aboutHTML');
function getHTMLAndStore(url, object, property) {
$http.get(url).then(function(response) {
var raw_html = response.data;
var weird_html = raw_html.replace(/</g,"<");
object[property] = weird_html;
}).catch(console.error.apply(console));
}
This is pure JS and not really related to Angular.
Note that these two requests will fire in parallel, not in sequence.
$http returns a promise so you need to return that promise from the function and use another then() to assign the scope variable:
function getHTML(url){
// return the promise
return $http.get(url).then(function(response) {
var raw_html = response.data.replace(/</g,"<");
return raw_html;
});
}
getHTML('pages/index.html').then(function(raw_html){
$scope.indexHTML = raw_html;
});
Currently your function doesn't return anything

One-time resolving promise singleton (Angular service)

The question applies to promises in general and isn't specific to Angular, but the example makes use of Angular $q and service singletons.
Here is a plunker
var app = angular.module('app', []);
app.factory('onetimeResolvingService', function ($q) {
var promise = $q(function(resolve, reject) {
setTimeout(function () {
resolve();
}, 500);
});
return promise;
});
app.controller('AController', function (onetimeResolvingService, $q) {
onetimeResolvingService.then(function () {
console.log('A resolved');
return $q.reject();
}).then(function () {
console.log('A resolved');
});
});
app.controller('BController', function (onetimeResolvingService, $q) {
onetimeResolvingService.then(function () {
console.log('B resolved');
return $q.reject();
}).then(function () {
console.log('B resolved');
});
});
and the document is
<body ng-app="app">
<div ng-controller="AController"></div>
<div ng-controller="BController"></div>
</body>
It will naturally output
A resolved
B resolved
What would be a good pattern to make the singleton promise resolve only the first time, i.e.
A resolved
and not the subsequent times?
Something like onetimeResolvingService.$$state.status = 2 could possibly can do the trick, but it looks like $q hack and smells bad.
What would be a good pattern to make the singleton promise resolve only the first time
To not to. One of the key facets of a promise is that once it's settled, it's settled, and both the settled state (resolved or rejected) and the value are at that point unchanging. See §2.1.2 and §2.1.3 of the A+ promises spec:
2.1.2 When fulfilled, a promise:
2.1.2.1 must not transition to any other state.
2.1.2.2 must have a value, which must not change.
2.1.3 When rejected, a promise:
2.1.3.1 must not transition to any other state.
2.1.3.2 must have a reason, which must not change.
If the callbacks added via then are not satisfied at some stage (e.g., your second hookup), it's not a promise. It's something...else.
T.J. Crowder is correct in that the functionality you're looking for in a promise does not exist. The question of how to achieve what you're looking for however can be found in a structure like below:
function OnetimeValue($q) {
var promisedValue = $q(function(resolve, reject) {
setTimeout(function () {resolve('The one time value');}, 500);
});
var valueGot = false;
this.GetValue = function GetValue() {
var res;
if (!valueGot) {
res = promisedValue;
} else {
res = $q(function(resolve, reject) {
resolve(null);
});
}
valueGot = true;
return res;
};
}
Assuming you new this once (as angular services do), GetValue() will return the promisified string upon the first call. Subsequent calls return null.
This plunker shows the above in action
Edit: Whoops, misread the question.
There is now a way to do this with EcmaScript promises. The static Promise.resolve() method takes a promise and waits on its value; if it has already been resolved, it simply returns the value.
For example, here's how we're using this method to make multiple calls to fetchQuery rely on a single async authentication call:
fetchQuery gets an auth token (JWT) with:
const authToken = await AuthToken.get();
And AuthToken looks like (TypeScript):
class AuthToken {
private static _accessTokenPromise: Promise<string>;
public static async get() {
if (!this._accessTokenPromise)
this._accessTokenPromise = this.AuthFunction(); // AuthFunction returns a promise
return await Promise.resolve(this._accessTokenPromise);
}
}
Or simply call then() twice on the same promise object per the OP's question to have two calls wait on the same async operation.
If you're using lodash you can simply use memoize like this:
const returnFirstRunResultAlways = _.memoize(async function(params) { ... })

AngularJS how to use $q in Typescript

I am new at AngularJs and very new at Typescript.
I included Typescript in my AngularJs project but couldn't handle a service where i return a $q(function(){...})
my code looks like:
function foo(request, monitor, currentMonitorPropertys) {
var currentChart;
return $q(function (resolve) {
$http(request).success(function (chartResponse) {
...
resolve(monitor);
}).error(function(response){
...
});
});
I work with VS2013(TypeScript), if i implement this method like above, there comes an compilererror: Value of type 'IQService' is not callable. Did you mean to include 'new'?
So how could I implement the function with Typescript.
Thank you for your answer.
There are several ways to return a promise... $http returns a promise with each of its ajax calls, $timeout also returns a promise.
That being said you want to return a promise based upon something other than a scheduled event ($timeout, $interval) via $q you can do this...
// assume $q is injected into your service/controller/factory
// create a defer object
var defer = $q.defer();
// do something...
if (doSomething()){
defer.resolve(); //something went right
else {
defer.reject(); //something went wrong
}
//make sure you return out the promise, so the consumer can act upon it.
return defer.promise;
Also, $q has some nice helper methods to return a promise that you can use when you stub out some logic;
// this will a promise that will resolve with the value provided
return $q.when({some: 'result'});
// this will return a promise that will reject with the error specified
return $q.reject('some error message');
$q isn't a function but a service. If you want a defer, you can use the following code for example:
var deferred = $q.defer();
$http(request).success(function (chartResponse) {
deferred.resolve(monitor);
}).error(function(response){
deferred.reject(response);
});
// return promise
return deferred.promise;
What you can keep in mind if you don't do anything else, you can just return the $http call, because it is a promise itself:
return $http(request);
As you're using $http (that already returns a promise) why not returning this promise directly? Simpler and faster.
function foo(request, monitor, currentMonitorPropertys) {
var currentChart;
return $http(request).then(function (chartResponse) {
//...
return monitor;
});
});
Then when consuming this service you could manage success and error from there, which makes for a tidier implementation:
// for ex. in a controller
foo().then(mySuccessCallback)
.catch(myErrorHandler)
You need to defer $q before resolve it This is code try this one
(function retriveDemoJsonData(){
angular.module('myApp').factory('actionData', function ($q, $http) {
var data={};
data.actionDataJson = function(id){
//The original business logic will apply based on URL Param ID
var defObj = $q.defer();
$http.get('demodata.json')
.then(function(res){
defObj.resolve(res.data[0]);
});
return defObj.promise;
}
return data;
});
})();
----------+
I hope this will help you......

What is the best way to implement promise in the given scenario?

I'm using D.js as promise library for our javascript application.
Following is my sample code:
function getData(deferred) {
var data_one;
// getInfo is returning a promise for async task
getInfo()
.then(function (resp_one) {
data_one = resp_one;
// getInfo2 is also returning another promise
return getInfo2();
})
.then(function (resp_two) {
deferred.resolve('prefix' + data_one + resp_two);
});
};
function sample () {
var d = D(),
data = localStorage.getItem('key');
if (data) {
d.resolve(data);
} else {
getData(d);
}
return d.promise;
}
sample().then(function (data) {
//do something with data.
});
I im invoking sample function. Is the implementation inside the sample function and sub functions following the coding standard for promises?
Im new to promises, Is it good to passing around deferred object to other function to resolve/reject ??
Is there any better way to implement the above functionality??
Thanks in advance..
Looks like you can improve the code if you use promises in more natural way.
First of all if getData returns a Promise then you don't have to pass deferred around, this is considered anti-pattern. You just simply return getInfo().
Another thing, in the sample function if your data might be already available it's convenient to use D.promisify method to return resolved promise with non-promise value:
function getData() {
var data_one;
return getInfo().then(function (resp_one) {
data_one = resp_one;
return getInfo2();
})
.then(function (resp_two) {
return 'prefix' + data_one + resp_two;
});
};
function sample() {
var data = localStorage.getItem('key');
return data ? D.promisify(data) : getData();
}
sample().then(function (data) {
//do something with data.
});

Immediately return a resolved promise using AngularJS

I'm trying to get my head around promises in JavaScript (in particular AngularJS).
I have a function in a service, let's call it fooService, that checks if we've loaded some data. If it has, I just want it to return, and if we haven't, we need to load the data and return a promise:
this.update = function(data_loaded) {
if (data_loaded) return; // We've loaded the data, no need to update
var promise = Restangular.all('someBase').customGet('foo/bar').then(function(data) {
// Do something with the data here
}
return promise;
}
I have another function that then calls the update function of fooService like so:
fooService.update(data_loaded).then(function() {
// Do something here when update is finished
})
My issue here is that if we don't need to load the data in the update function, a promise isn't returned, so the .then() is not called in my other function. What should the approach be here - basically I want to return a resolved promise immediately from the update() function if we do not need to get data from the Restangular call?
As your promise use the same syntax as the JavaScript native one, you could use and return an already resolved JavaScript promise : Promise.resolve()
return(Promise.resolve("MyReturnValue"));
The current accepted answer is overly complicated, and abuses the deferred anti pattern. Here is a simpler approach:
this.update = function(data_loaded) {
if (data_loaded) return $q.when(data); // We've loaded the data, no need to update
return Restangular.all('someBase').customGet('foo/bar')
.then(function(data) {
// Do something with the data here
});
};
Or, even further:
this._updatep = null;
this.update = function(data_loaded) { // cached
this._updatep = this._updatep || Restangular.all('someBase') // process in
.customGet('foo/bar'); //.then(..
return this._updatep;
};
AngularJS's $q service will help you here. It is much like Kris Kowal's Q promise library.
When you have an async method that may return a promise or value use the $q.when method. It will take what ever is passed to it, be it a promise or a value and create a promise that will be resolved/rejected based on the promise passed, or resolved if a value is passed.
$q.when( fooService.update(data_loaded) ).then(function(data){
//data will either be the data returned or the data
//passed through from the promise
})
and then in your update function return the data instead of just returning
if (data_loaded) return data_loaded;
Similar to Elo's answer, you can return an already resolved promise using the async/await syntax:
this.update = async (data_loaded) => {
if (data_loaded)
return await null; // Instead of null, you could also return something else
// like a string "Resolved" or an object { status: 200 }
else
return await OtherPromise();
}
You could use the $q.defer() like this:
this.update = function (data_loaded) {
var deferred = $q.defer();
if (data_loaded) {
deferred.resolve(null); // put something that your callback will know the data is loaded or just put your loaded data here.
} else {
Restangular.all('someBase').customGet('foo/bar').then(function(data) {
// Do something here when update is finished
deferred.resolve(data);
}
}
return deferred.promise;
};
Hope this helps.

Categories

Resources