How to resolve my promise calls in my case - javascript

I was trying to reduce my promise objects in my service. I have something like
angular.module('myApp').service('testService', ['Employees','$q',
function(Employees, $q) {
var service = {};
var firstEmp;
var deferred = $q.defer();
Employees.query({
Id: 123
}, function(objects) {
firstEmp = objects[0];
deferred.resolve(objects);
})
service.getFirstEmployee = function() {
var deferredtwo = $q.defer();
// return deferredtwo.promise;
//How to solve the double promise defer in my case
//First promise is wait for the whole employees
//this one is to return first employee
deferred.promise.then(function(){
deferredtwo.resolve(firstEmp);
})
return deferredtwo.promise;
}
return service;
]);
Controller
testService.getFirstEmployee.then(function(firstEmployee){
console.log(firstEmployee) <---show first employee
})
I am not sure how to resolve the double promise objects. Can anyone help me? Thanks a lot!

If your ultimate objective is just to get the first employee, then you don't need all this "double promise" stuff at all. Just resolve one promise with the first employee:
angular.module('myApp').service('testService', ['Employees','$q',
function(Employees, $q) {
var pFirstEmployee = $q(function (resolve) {
Employees.query({ Id: 123 }, function(objects) {
resolve(objects[0]);
});
});
return {
getFirstEmployee: function() {
return pFirstEmployee;
}
};
}
]);
If you want two methods - one that returns a promise for all employees returned from the query, and another that returns just the first one from that set, just create a promise for all the employees and chain off of that:
angular.module('myApp').service('testService', ['Employees','$q',
function(Employees, $q) {
var pAllEmployees = $q(function (resolve) {
Employees.query({ Id: 123 }, resolve);
}),
pFirstEmployee = pAllEmployees.then(function (employees) {
return employees[0];
});
return {
getAllEmployees: function() {
return pAllEmployees;
},
getFirstEmployee: function() {
return pFirstEmployee;
}
};
}
]);

After the clarification I guess this is what you want:
angular.module('myApp').service('testService', ['Employees','$q',
function(Employees, $q) {
var service = {};
var deferred = $q.defer();
Employees.query({Id: 123}, function(objects) {
firstEmp = objects[0];
deferred.resolve(objects);
});
service.getFirstEmployee = function() {
return deferred.promise.then(function(employees){
return employees[0];
});
}
return service;
]);

Related

Return promises in order

I'm facing an issue to return promises using $q#all method.
I want to make promises dependent on each other, i.e.:
If I set obj1, obj2 and obj3 I want to get them in the same order.
How can I achieve this?
Factory:
mainFactory.$inject = ['$http', '$q'];
function mainFactory($http, $q) {
var mainFactory = {
getPromises: getPromises
};
return mainFactory;
function getPromises(id) {
promises = {
'obj1': $http.get('http1'),
'obj2': $http.get('http2'),
'obj3': $http.get('http3'),
'obj4': $http.get('http4', { params: { 'id': id } }),
'obj5': $http.get('http5'),
'obj6': $http.get('http6', { params: { 'id': id } })
};
return $q.all(promises);
}
}
Controller:
MainCtrl.$inject = ['mainFactory'];
function MainCtrl(mainFactory) {
var vm = this;
mainFactory.getPromises(id)
.then(getResponse)
.catch(getError);
function getResponse(response) {
var keys = Object.keys(response), i = keys.length;
while (i--) {
var key = keys[i];
console.log(key); // I want all the keys in order, i.e. => obj1, obj2.. and so on
var value = response[key].data;
switch(key) {
...
}
}
}
function getError(error) {
console.log(error);
}
}
EDIT:
I tried this way also:
var promises = {};
return $http.get('/admin/http1.json').then(function (value) {
promises['obj1'] = value;
})
.then(function (result) {
return $http.get('/admin/http2.json').then(function (value) {
promises['obj2'] = value;
});
}).then(function (result) {
return $http.get('/admin/http3.json').then(function (value) {
promises['obj3'] = value;
});
});
return $q.all(promises);
Using $q.all will resolve each promise in no particular order. If you want them to execute after each promise has been resolve, use promise chaining.
function getPromises(id) {
var getObjA = function () {
return $http.get('http1');
};
var getObjB = function () {
return $http.get('http2');
};
var getObjC = function () {
return $http.get('http3');
};
var getObjD = function () {
return $http.get('http4', { params: { 'id': id } });
};
var getObjE = function () {
return $http.get('http5');
};
var getObjF = function () {
return $http.get('http5', { params: { 'id': id } });
};
return getObjA()
.then(getObjB)
.then(getObjC)
.then(getObjD)
.then(getObjE)
.then(getObjF);
}
Edit: as an additional info, you can catch any error in any of those promise by placing a catch statement here
getPromises("id")
.then(<success callback here>)
.catch(<error callback that will catch error on any of the promises>);
Meaning, once a promise fails, all the succeeding promises below wouldn't be executed and will be caught by your catch statement
Edit 2
Mistake, I just copied you code above without realizing it was an object. LOL.
promises = [
$http.get('http1'),
$http.get('http2'),
$http.get('http3'),
$http.get('http4', { params: { 'id': id } }),
$http.get('http5'),
$http.get('http6', { params: { 'id': id } })
]
Edit 1
Sorry I didn't notice the comments Jared Smith is correct.
Object keys are inherently unordered. Use an array instead.
Edit 0
Object keys wont be ordered. Use array on declaring your promises.
promises = [
$http.get('http1'),
$http.get('http2'),
$http.get('http3'),
$http.get('http4', { params: { 'id': id } }),
$http.get('http5'),
$http.get('http6', { params: { 'id': id } })
]
$q.all(promises)
.then(functions(resolves){
// resolves here is an array
}).catch(function(err){
// throw err
});

Testing promise in Jasmine and not resolving- getting Expected undefined to be object

I am new to testing and I am testing some angular code with jasmine. The mock promise I have created isn't resolving. Here is the block of code that I am testing:
$scope.init = function() {
EngageService.getGrouping($stateParams.gid).then(function(res) {
$scope.grouping = res.data;
$scope.parent_id = $scope.grouping.parent != null ? $scope.grouping.parent.id : null;
$scope.startDate = $scope.grouping.startDate ? new Date($scope.grouping.startDate) : new Date()
$scope.endDate = $scope.grouping.endDate ? new Date($scope.grouping.endDate) : new Date()
$scope.nda = $scope.grouping.nda == "No" ? false : true;
$scope.grouping.invitation = $scope.grouping.invitateOnly == true ? 'true' : 'false';
$scope.canManageGrouping = UserService.memberships[$scope.grouping.id].role.canManageGrouping;
$scope.canCreateGroup = UserService.memberships[$scope.grouping.id].role.canCreateGroup;
})
EngageService.getProjects().then(function(res) {
$scope.projects = res.data.rows;
})
}
and here is the code for testing the promise
describe('Tests for WorkspaceEdit', functio
var getGroupingPromise;
beforeEach(function() {
module('iq4App');
return inject(function($injector) {
this.EngageService = {
getGrouping: function(gid) {
getGroupingPromise = $q.defer();
console.log(getGroupingPromise.promise)
return getGroupingPromise.promise;
}
};
this.scope = this.rootScope.$new();
this.scope.$digest();
}); '$upload': this.upload,
'ResourceService': this.ResourceService,
'MatchService': this.MatchService
});
this.scope.$digest();
});
});
});
it('should set stuff on init', function() {
var fakeData = {
data: {
parent: {
id: 3
}
}
}
this.scope.init();
this.scope.$digest();
getGroupingPromise.resolve(fakeData.data)
expect(this.scope.grouping).toBe(fakeData.data);
})
});
I am trying to resolve some fake data to $scope.grouping but the data is resolving as undefined. I know the promise is being created from my console.log in the getGrouping method in this.EngageService. I've stripped out a lot of code that I didn't think was relevant but let me know if you want more detail.
Thank you!
You need to resolve the promise with the fake data:
getGroupingPromise = $q.defer();
getGroupingPromise.resolve(fakeData.data);
return getGroupingPromise.promise;
The digest in my test had to go after the resolve and not before.

Returning a value in AngularJS

I wrote an angular service which querys a db and should return the Categories:
(function() {
'use strict';
angular.module('budget')
.service('CategoriesService', ['$q', CategoriesService]);
function CategoriesService($q) {
var self = this;
self.loadCategories = loadCategories;
self.saveCategorie = saveCategorie;
self.datastore = require('nedb');
self.db = new self.datastore({ filename: 'datastore/default.db', autoload : true});
function saveCategorie (categorie_name) {
var entry = {name: categorie_name,
type: 'categorie'}
self.db.insert(entry);
};
function loadCategories () {
self.db.find({type: 'categorie'}, function (err, docs) {
var categories = docs;
return categories;
});
};
return {
loadCategories: self.loadCategories,
saveCategorie: self.saveCategorie
};
}
})();
When I console.log inside the function loadCategories() it returns me an array of 6 objects (the objects from the database) but outside of the function it just gives me undefined.
I am calling via the controller with CategoriesService.loadCategories()
So I think I might have to do something thas called promise but Iam not sure about that.
How can I get acctual data back from this service?
First of all you don't need to return anything from the service factory recipe, you just need to assign a method to the this variable.
At least, you need:
// service.js
self.loadCategories = function() {
var deferred = $q.defer();
db.find({type: 'categorie'}, function (err, docs) {
deferred.resolve(docs);
});
return deferred.promise;
};
// controller.js
service
.loadCategories()
.then(function(categories) {
$scope.categories = categories;
})
;
you need to return your promise first so just add one more return and you are good to go...
function loadCategories () {
// you need to return promise first and you can resolve your promise in your controller
return self.db.find({type: 'categorie'}, function (err, docs) {
var categories = docs;
return categories;
});
};

How can I get only the object with my arrays returned from a function with a promise?

I want the data in res passed to my notes variable. But it's returning a bigger nested object. Why is it happening?
If I inspect in the console the value of cleanArrayOfNotes I get the object that I want, but once its assigned to notes it becomes a quite bigger object. I understand it's part of the nature of the Promises, which at the moment I still trying to understand. Any help?
notes_service.js
var notesService = {notesObjectInService: [], newNote: null};
notesService.getAll = function() {
return $http.get('/notes.json').success(function(data){
//console.log(data)
angular.copy(data, notesService.notesObjectInService);
//console.log(notesService)
})
};
navCtrl.js
var notes = notesService.getAll().then(function(res){
var cleanArrayOfNotes = res.data;
//navCtrl line12
console.log(cleanArrayOfNotes);
return cleanArrayOfNotes;
});
//navCtrl line16
console.log(notes);
This should work for you:
notes_service.js
app.factory ('NoteService', function($http) {
return {
getAll: function() {
return $http.get('/notes.json').then(function(response) {
return response.data;
});
}
}
});
navCtrl.js
NotesService.getAll().then(function(res){
$scope.cleanArrayOfNotes = res.data;
});
Or, if you want to return the result rather than the promise, you can:
notes_service.js
app.factory ('NoteService', function($http) {
var notes = [];
var promise = $http.get('/notes.json').then(function(response) {
angular.copy(response.data, notes);
return notes;
});
return {
getAll: function() {
return notes;
},
promise: promise
}
});
navCtrl.js
// get array of notes
scope.cleanArrayOfNotes = NotesService.getAll();
// or use promise
NoteService.promise.then(function(response) {
scope.cleanArrayOfNotes = response.data;
});

Accessing a variable initialized via a Factory which launches an async request

I've got a factory that sends a POST request to get some JSON key-value pairs:
.factory('Rest', ['$resource',
function($resource) {
// returns JSON key-value pairs, e.g. "{'foo', 'bar'}"
return $resource('rest/get', {}, {
get: {
method: 'POST'
}
});
}])
I've got another factory intended to be exposed to controllers in order to access a key-value pair given its key:
.factory('Pairs', ['Rest',
function(Rest) {
var pairs;
Rest.get(function(response) {
pairs = response;
});
return {
get: function(key) {
return pairs[key];
}
};
}])
The problem is that, when I call Pairs.get('foo'), the pairs object of the Pairs factory is not initialized yet (since Rest.get is asynchronous), and thus results in a TypeError: Cannot read property 'foo' of undefined:
.controller('AnyController', ['Pairs',
function (Pairs) {
console.log(Pairs.get('foo')); // error
}])
Any idea how to solve this?
As you stated in your question, Rest.get is asynchronous, so your Pairs.get has to be asynchronous too. You can implement it as the following:
.factory('Pairs', ['Rest', '$q',
function(Rest, $q) {
var pairs;
var deferredList = [];
Rest.get(function(response) {
pairs = response;
angular.forEach(deferredList, function(o) {
o.deferred.resolve(pairs[o.key]); // resolve saved defer object
});
deferredList = null; // we don't need the list anymore
});
return {
get: function(key) {
if (pairs) {
return $q.when(pairs[key]); // make immediate value as a promise
}
var deferred = $q.defer(); // create a deferred object which will be resolved later
deferredList.push({ // save both key and deferred object for later
key: key,
deferred: deferred
});
return deferred.promise;
}
};
}])
Then use it like this:
Pairs.get('foo').then(function(value) {
console.log(value);
});
You want to wrap your async function in a promise. Here's how I've done something similar.
Note: safeApply triggers the $digest cycle, if necessary, so that angular can react to any data changes it might be watching.
var safeApply = function (scope, fn) {
if (scope.$$phase || scope.$root.$$phase) {
fn();
} else {
scope.$apply(fn);
}
};
ret.getAll = function(type) {
var deferred = $q.defer();
var where = "apm_type = '" + type + "'";
query(type, where, function(err, response) {
var objs = [];
if (err) {
safeApply($rootScope, function() { deferred.reject(err);});
} else {
safeApply($rootScope, function() { deferred.resolve(response);});
}
});
return deferred.promise;
};

Categories

Resources