I am just starting to learn Angularjs so i might be using the whole thing wrong but please correct me.
I have the following factory that goes like this:
app.factory('MenuService',['service1','service2','service3',function(service1,service2,service3){
var var1 = [],
var2 = [],
var3 = [];
service1.getDataMethod(function(data){
// processes data and saves it in var1
});
service2.getDataMethod2(function(data)){
});
/// same goes for service3.
return {"prop2": var1, "prop2" : var2, "prop3": var3};
}])
I need to process the data return by service2 , based on data returned in the first service but every time i try to access it, the variable is empty.
I know the functions return a promise, is there a way to tell the second function to wait for the first one to finish in order to be able to use the data it brings?
I hope i made myself understood. Let me know if i should add something else.
Like this?
service1.getDataMethod(function(data){
// processes data and saves it in var1
}).then(function(data) {
return service2.getDataMethod2(data)
})
Basically each promise have .then() method. So you can chain this as much as you need a.then().then().then()
In addition, some promise implementations have method wrappers such as .success() and .error(). These methods works similar to .then()
in angular you have access to $q service that is implementation of q library that works with promises. https://docs.angularjs.org/api/ng/service/$q
If you don't like it for some reason try async
Basically you have to chain your promises:
service1.getDataMethod(function(data){
return something; // this is important, only that way you will be
// able to use the next .then()
})
.then(function(something) { // what you returned previously will be the function's argument
return service2.getDataMethod2(data);
})
.then(function(result) { // `result` is what getDataMethod2 resolves
})
.catch(function(err) {
// very important to add .catch to handle errors
});
Related
Lets say I have the following code:
var myData = [];
var future = Promise.All([getPromise1, getPromise2]).then((result)=>{
var myData = result []
}).catch((err)=>{
\\Do something with err
})
doSomething (myData);
Can I do something with myData after it has received the data from promise.all result? Or do I have to handle subsequent code inside of a promises .then().
For Example:
future.then((data)=>{
doSomething(myData);
});
I feel like I would have to create a bunch of .then()'s in order to keep handling any type of synchronous code. Although I suppose since myData is the result then any code that uses the data in result should be handled as part of that promise like:
var future = Promise.All([getPromise1, getPromise2]).then((result)=>{
doSomething(result [])
}).catch((err)=>{
\\Do something with err
})
Am I just understanding this all wrong?
Looking forward to thoughts and opinions!
P.S im working on node.js
You're correct, the result of the promise can only be used within the promise's .then.
Side note - subsequent promises don't necessarily need to be nested, instead they can be chained. For example:
myPromise(foo).then(bar => {
// Do something with the result
baz = bar * 2
// Pass the result to some other promise
return mySecondPromise(baz)
}).then(foo2 => {
// Output the result of the second promise
console.log(foo2)
}).catch(err => {
console.error(err)
});
Errors will propagate down the chain.
You will have to handle subsequent code inside the .then() function callback. The reason you can't do anything outside of the promise chain is because of the way Promises work. a quick Google search about asynchronous javascript gives an explanation of why this is. The short answer is that doSomething(myData) will run before the code in the .then() function is run, which means that myData will still be an empty array when the script reaches that point in the code.
Actually even if the code did somehow reach the .then() function before the doSomething(myData) method, myData would still be an empty array because the myData declared in the .then() function is another variable entirely, since it is declared as
var myData
If you want to write code that looks more synchronous, I suggest using async/await, depending on if your version of node.js supports it or if you're using a transpiler. With async/await your code would look like:
async function() {
try {
var myData = await Promise.all([getPromise1, getPromise2]);
doSomething(myData);
} catch (ex) {
// Do something with err
}
}
I'm not so familiar with promises.
I would like hide promise-implementation from promise-call.
Example:
function findFriends(req, res) {
const promiseFriend = MyFriendes.find({}).exec(); //call promise
if(friends.length===0){
logger.warn('No friendsavailible');
}else if(friends === undefined){
res.status(500).json({
error: 'INTERNAL ERROR'
});
}else{
res.status(200).json({
friends: friends
});
}
}
and I will resolve my promise within same file but not
at same function, where I call this promise.
promiseFriend
.then(function(friends){
return friends;
})
.catch(function(err){
logger.error({error:err});
});
Now, I get, that "promiseFriend" is undefined.
How can I separate promise-call from promise-resolution?
If you want to define a promise in a function and use it somewhere else then first of all you need to return the promise from that function, which you're not doing in your code. Then you need to actually call that function which you are also not doing. And finally you need to use a then callback on the returned value, which you are not doing in this case as well.
There is no point in saving the promise in a local variable promiseFriend that is scoped to this function. There is also no point to return a value in your then callback: .then(function (friends) { return friends; }) - I have no idea what have tried to do here.
I suppose that findFriends is supposed to be a route handler for Express. If so then make sure that you send a response in every case (which you don't do for friends.length===0). Also, you need to actually add a then handler to the saved promise if you want to act when it's resolved. Right now you don't even have friends defined in your function. Also add a catch handlers and also send a response for that case.
Then you might return the promise from your function but not if it is a route handler, it doesn't make sense. You can return a promise from a function:
function x() {
return MyFriendes.find({}).exec();
}
and then use it:
x().then(friends => ...).catch(error => ...);
but you cannot use return values if you don't return it, you can't use undefined variables as if they were defined, and you actually need to consider who is your return value returned to.
I suggest that you learn how Node actually works because it seems that you have copied and pasted some random code, joined it together and expect that it does what you want without actually trying to understand it.
To get a better understanding on the asynchronous nature of Node that is giving this execution order here, see those answers:
A detailed explanation on how to use callbacks and promises
Explanation on how to use promises in complex request handlers
An explanation of what a promise really is, on the example of AJAX requests
An explanation of callbacks, promises and how to access data returned asynchronously
Don't try to write Node programs before you understand the concept of function calls, return values, callbacks and in this case promises.
Hello everyone out there. I've hit a brick wall. I'm trying to research Angular async method calls, promise objects and implement a solution to a particular issue. After a few hours of trial and error and restructuring my code, I'm certain at this point the answer is right under my nose but I cannot see it.
I have a scenario where I must use $http to make a backend service call, which yields a promise. In the results of promise I will receive a data object with one string property and an array of 100 IDs. This backend service will only deliver payloads of 100 IDs at a time, so I must make x number of $http calls to reach the end of the list, as provided by this backend service. As I understand promises, I must evaluate the $http response in the promise's .then() method and if the string property is 'N' then I know I must call the backend service again for another batch of 100 IDs. After all IDs have been delivered the string will contain a 'Y' indicating that the end of this has been sent and don't call again. No comment required on this scheme, I know it's fairly lame, and unfortunately out of my control. ;-)
Everything I've studied regarding promises just seem to illustrate chaining of promises, in a linear fashion, if more async calls are needed. Whether the promise chain is nested or flat, it seems like I can only make a "known" or fixed number of sequential calls, e.g. Promise1 to Promise2, to Promise3, etc. Forgive my limited experience with promises here.
Here is a code example, you can see where I'm stuck here, nesting downward. I can't just "hope" after 3 or 5 or 10 calls I'll get all the IDs. I need to dynamically evaluate the result of each call and call again.
Here is the Angular service which invokes $http
(function() {
'use strict';
angular
.module('myapp')
.factory('IDservice', IDservice);
function IDservice($http) {
var service = {
model: {
error: {
state: false,
message: ''
},
ids: [],
end: '',
},
GetIDs: function() {
var request = 'some params...';
var url = 'the url...';
return $http.post(url, request).then(reqComplete).catch(reqFailed);
function reqComplete(response) {
service.model.ids = response.data.idList;
service.model.end = response.data.end;
return service.model;
}
function getIntradayMsrFailed(error) {
service.model.error.state = true;
service.model.error.message = error;
return service.model;
}
}
};
return service;
}
})();
Here is the Controller which invokes the Angular service, which ultimately drives some UI elements on the respective view:
(function() {
'use strict';
angular
.module('myapp')
.controller('AvailableIDsController', AvailableIDsController);
function AvailableIDsController($scope, $location, $timeout, IDservice) {
var vm = this;
vm.completeIDList = [];
activate();
function activate() {
IDservice.GetIDs().then(function(response){
vm.completeIDList = response.ids;
console.log('promise1 end');
if(response.end === 'N'){
IDservice.GetIDs().then(function(response){
angular.forEach(response.ids,function(nextID){
vm.comleteIDList.push(nextID);
});
console.log('promise2 end');
if(response.end === 'N'){
IDservice.GetIDs().then(function(response){
angular.forEach(response.ids,function(nextID){
vm.comleteIDList.push(nextID);
});
console.log('promise3 end');
});
}
});
}
});
console.log('mainthread end');
}
}
})();
You can see where that's going... and it's very, very ugly.
I need a way, inside the activate() method, to call a method which will take care of invoking the service call and return the result back up to activate(). Now, still in the activate() method, evaluate the result and determine whether to call again, etc. Where I'm stuck is once the main processing thread is done, you're left with program control in that first promise. From there, you can perform another promise, etc. and down the rabbit hole you go. Once I'm in this trap, all is lost. Clearly I'm not doing this right. I'm missing some other simple piece of the puzzle. Any suggestions would be so greatly appreciated!
You're looking for plain old recursion:
function AvailableIDsController($scope, $location, $timeout, IDservice) {
var vm = this;
vm.completeIDList = [];
return activate(0);
function activate(i) {
return IDservice.GetIDs().then(function(response) {
[].push.apply(vm.completeIDList, response.ids); // simpler than the loop
console.log('promise'+i+' end');
if (response.end === 'N'){
return activate(i+1);
}
});
}
}
Don't forget the returns so that the promises chain together and you can wait for the end of the requests.
Question:
Is there an "easy" way to cancel ($q-/$http-)promises in AngularJS or determine the order in which promises were resolved?
Example
I have a long running calculation and i request the result via $http. Some actions or events require me to restart the calculation (and thus sending a new $http request) before the initial promise is resolved. Thus i imagine i can't use a simple implementation like
$http.post().then(function(){
//apply data to view
})
because I can't ensure that the responses come back in the order in which i did send the requests - after all i want to show the result of the latest calculation when all promises were resolved properly.
However I would like to avoid waiting for the first response until i send a new request like this:
const timeExpensiveCalculation = function(){
return $http.post().then(function(response){
if (isNewCalculationChained) {return timeExpensiveCalculation();}
else {return response.data;}
})
}
Thoughts:
When using $http i can access the config-object on the response to use some timestamps or other identifiers to manually order the incoming responses. However i was hoping I could just tell angular somehow to cancel an outdated promise and thus not run the .then() function when it gets resolved.
This does not work without manual implementation for $q-promises instead of $http though.
Maybe just rejecting the promise right away is the way to go? But in both cases it might take forever until finally a promise is resolved before the next request is generated (which leads to an empty view in the meantime).
Is there some angular API-Function that i am missing or are there robust design patterns or "tricks" with promise chaining or $q.all to handle multiple promises that return the "same" data?
I do it by generating a requestId, and in the promise's then() function I check if the response is coming from the most recent requestId.
While this approach does not actually cancel the previous promises, it does provide a quick and easy way to ensure that you are handling the most recent request's response.
Something like:
var activeRequest;
function doRequest(params){
// requestId is the id for the request being made in this function call
var requestId = angular.toJson(params); // I usually md5 hash this
// activeRequest will always be the last requestId sent out
activeRequest = requestId;
$http.get('/api/something', {data: params})
.then(function(res){
if(activeRequest == requestId){
// this is the response for last request
// activeRequest is now handled, so clear it out
activeRequest = undefined;
}
else {
// response from previous request (typically gets ignored)
}
});
}
Edit:
On a side-note, I wanted to add that this concept of tracking requestId's can also be applied to preventing duplicate requests. For example, in my Data service's load(module, id) method, I do a little process like this:
generate the requestId based on the URL + parameters.
check in requests hash-table for the requestId
if requestId is not found: generate new request and store promise in hash-table
if requestId is found: simply return the promise from the hash-table
When the request finishes, remove the requestId's entry from the hash-table.
Cancelling a promise is just making it not invoke the onFulfilled and onRejected functions at the then stage. So as #user2263572 mentioned it's always best to let go the promise not cancelled (ES6 native promises can not be cancelled anyways) and handle this condition within it's then stage (like disregarding the task if a global variable is set to 2 as shown in the following snippet) and i am sure you can find tons of other ways to do it. One example could be;
Sorry that i use v (looks like check character) for resolve and x (obvious) for reject functions.
var prom1 = new Promise((v,x) => setTimeout(v.bind(null,"You shall not read this"),2000)),
prom2,
validPromise = 1;
prom1.then(val => validPromise === 1 && console.log(val));
// oh what have i done..!?! Now i have to fire a new promise
prom2 = new Promise((v,x) => setTimeout(v.bind(null,"This is what you will see"),3000));
validPromise = 2;
prom2.then(val => validPromise === 2 && console.log(val));
I'm still trying to figure out a good way to unit test this, but you could try out this kind of strategy:
var canceller = $q.defer();
service.sendCalculationRequest = function () {
canceller.resolve();
return $http({
method: 'GET',
url: '/do-calculation',
timeout: canceller.promise
});
};
In ECMA6 promises, there is a Promise.race(promiseArray) method. This takes an array of promises as its argument, and returns a single promise. The first promise to resolve in the array will hand off its resolved value to the .then of the returned promise, while the other array promises that came in second, etc., will not be waited upon.
Example:
var httpCall1 = $http.get('/api/something', {data: params})
.then(function(val) {
return {
id: "httpCall1"
val: val
}
})
var httpCall2 = $http.get('/api/something-else', {data: params})
.then(function(val) {
return {
id: "httpCall2"
val: val
}
})
// Might want to make a reusable function out of the above two, if you use this in Production
Promise.race([httpCall1, httpCall2])
.then(function(winningPromise) {
console.log('And the winner is ' + winningPromise.id);
doSomethingWith(winningPromise.val);
});
You could either use this with a Promise polyfil, or look into the q.race that someone's developed for Angular (though I haven't tested it).
I am so sorry if the other promise threads have answered this but when looking at some of them I am just not getting the answer to solve my issue. I have three json files that I want to grab, parse and manually merge. The problem is I am getting stuck in promise jail. Let me show you some of the code from my angularjs controller.
$scope.tests = [];
$scope.tests = $http.get('results/testResults.json').then(function(res) {
return res;
});
console.dir($scope.tests);
From the console.dir I am getting a promise but what I was hoping for was the data from the res variable. There has to be some way to get that data out. Is there no way to get that data out of the promise to a global variable so other promises of functions can use this data? Thanks
The promise completes some time in the future. You are examining the promise variable before the data is in the promise. You should stay in the promise chain to use your data. Outside the promise chain, you don't know the timing of the asynchronous events (that's why you use promises in the first place).
If you really don't want to use the data right in your first .then() handler which is the ideal place to use it, then you can chain another .then() onto your promise:
$scope.tests = $http.get('results/testResults.json');
$scope.tests.then(function(data) {
// can use data here
});
FYI, promises do not populate data into global variables. They make the data available to use in .then() callbacks when those callbacks are called.
Make up an an property and stick it on the window object, like this,
window.myProperty
Then pass your data to it, from inside the promise, like this,
window.myProperty = data;
And pick it up, when you are back outside, like this,
myVariable = window.myProperty;
Or the reverse, because I cannot seem to get data in or out of some Promises, so that would go like this,
Again, make up a property and stick it on the window object, like this,
window.myProperty
Then pass your data to it, from outside the promise, like this,
window.myProperty = data;
And pick it up, when you are inside, like this,
myVariable = window.myProperty;
This cannot be the best way to do this! Does anyone know a better way?