Is any way that $promise can execute multiple times or any alternate in angular.
Refer below code.
Sample Code:-
promise.then(function(greeting) {
alert('Success: ' + greeting);
}
...
if () {
resolve('Hello, ' + name + '!');
} else {
reject('Greeting ' + name + ' is not allowed.');
}
Yup, it will only execute only once, might be in angularJS you will not get alternative. You can try jQuery or some thing else. :)
You could probably use the notify callback
notify(value) - provides updates on the status of the promise's execution. This may be called multiple times before the promise is either resolved or rejected.
The following (untested) code should notify ten times with the numbers 1-10 and then resolve with the message "Done counting".
function countToTen() {
return $q(function(resolve, reject, notify) {
for (var counter=1; counter <= 10; counter++) {
setTimeout(function() {
notify(counter);
}, 1000 * counter);
}
setTimeout(function() { resolve("Done counting"); }, 10001);
});
}
Edit : as ARedHerring notices, notify isn't part of any promise standard and should be considered bad practice.
The contract of a promise is that it will only ever resolve once, and honestly I can't really imagine any scenarios where it makes sense for it to execute more than once. After a promise has resolved, any subsequent calls to then will be invoked upon the next event loop tick as the value of the promise has already been resolved and will be cached.
This seems like an XY problem. Could you explain what you are trying to do in more detail?
You can go through this forum where Thomas has told how you can resolving a promise multiple times.
$rootScope.$apply( function() {
deferred.resolve( incremented );
});
Related
Just a 'silly' question but it's confusing me a lot, for a student of AngularJS.
When console.logging a variable, let's say var result is a promise from a GET or whatever, and .then() I do some tasks, like asignin a variable.
var result = ... ;
$scope.number = {};
result.then(function(data) {
$scope.number = 5;
console.log($scope.number);
});
console.log($scope.number);
Okay!
So to me, this should print
5
5
(In case I initialise a $scope variable, outside this function or promise)
5
undefined
(in case the second .log tries to print a non-defined global variable, outside the function)
So I get:
undefined
5
Why does the outter $scope.number print before the inner (5) variable?
I want to initialise the variable after getting the GET, within the promise, and later on, use the initialised var.
Thank you =) This will speed up my learning!
The main thing to understand is that the following returns a promise immediately:
var result = ... ;
Right after that, you pass a function to the .then method of the promise:
result.then(...)
And right after that you do:
console.log($scope.number);
Then later - once the asynchronous action (like an HTTP request) completes and the promise resolves - the function you passed to .then finally executes:
function(data) {
$scope.number = 5;
console.log($scope.number);
}
So promises exist to help you manage asynchronous behavior. They really start to shine when you have to manage multiple asychronous functions:
asyncFunction1().then(function() {
// This executes after asyncFunction1 completes
return asyncFunction2();
}).then(function() {
// This executes after asyncFunction2 completes
});
Preamble
I had a thought: What if I reduced my duplicated data structures by stringing together my objects as microservices, and why don't I use Sails, nodejs and api calls. This is where it all started.
The problem definition
How can I send a dynamic array to a function, execute an unknown number of API calls to another microservice, then combine the returned values into a single object for processing?
The Journey
I have found material about q or bluebird.
I have a function (in sails) that returns me a list of users email addresses in a JSON array.
getUserInfo: function(opt, callback){
var https = require('https');
var options = {
hostname: opt.hostname || 'as.net.au',
port: opt.port || 443,
path: opt.path || '/developers',
method: opt.method || 'GET',
headers: opt.headers || {'Authorization': 'Basic ' + 'ThuperThecretKey'}
}
var req = https.request(options, function(res) {
var data = '';
res.setEncoding('utf8');
res.on('data', function(chunk) {
//console.log('BODY: ' + chunk); //oh so chunky
data += chunk;
});
res.on('end', function() {
//console.log(data)
callback(null, data)
})
})
req.on('error', function(e) {
console.log('problem with request: ' + e.message)
callback(e) //error
})
req.end()
}
It gives me back a set of email addresses in a JSON array:
['dev1#thisplace.com','dev2#thisplace.com','dev3#someotherplace.com']
and I stick this into a json object called "devs".
So now I want to pass this array to a function and have it execute an API call for every dev, and add the results into either the json object, or a new one. I assume a new one as devs is an array not a complete object.
The consensus from the Internet seems to be to use bluebird, but I am struggling with what I assume everyone struggles with, what variable is named what, which one gets updated, how to load things into the variable, where it goes, etc, etc, my head hurts. There are things going in entrances that I am not sure should be going there.
So I am asking for help. Does anyone have a example of the best way to do these asynchronously and then parse the results?
I got far enough to see a nice loop of asynchronous calls going out to my API (thats below), but how do I pass in and read a variable in the last then function?
It logged "Executing a promise for dev1#thisplace.com..... etc etc"
Then the "devs" object empty and the "dev" object as expected (each email address).
But how do I pass back to the calling json (or a new json) the output from My API call? How does it maintain and asynchronously update an object?
I assume something goes in the else after the error (Potential placeholder number 1).
Something like:
promise.map(devs, function(dev) {
console.log('executing a promise for ' + dev)
MyCont.getUserInfo({'path': '/developers' + dev}, function (err,devs) {
if (err) {
console.log(err)
} else {
console.log ('devs: ' + devs)
console.log('dev: ' + dev)
//Potential placeholder number 1 -Some here that loads the returned value to the passed in or new array?
}
})
}).then(function() {
//Or maybe it goes here?
console.log("done");
});
You touched on the first obvious solution, using Bluebird's map. First thing would be to use promises everywhere:
// make this a Promise
getUserInfo: function(opt) {
// your new best friend for issuing http requests without callbacks
var request = require('request-promise');
// the options have to change a little
var options = {
uri: 'https://'
+ opt.hostname || 'as.net.au' + ':'
+ opt.port || 443
+ opt.path || '/developers',
method: opt.method || 'GET',
headers: opt.headers || {'Authorization': 'Basic ' + 'ThuperThecretKey'}
}
// much shorter, right?
return request(options);
}
devs.map(function(dev) {
console.log('executing a promise for ' + dev)
return MyCont.getUserInfo({'path': '/developers' + dev});
}).then(function(results) {
// your results are here, in order
console.log(results);
});
I am going to assume, after reading all that, your question is this:
How can I resolve an existing promise using the results of a bunch of asynchronous functions calls?
First, start with the function inside your outermost promise:
var processEmailAddressList = function (addresses) {
// This is inside a promise.
// It will return when all addresses have been processed.
return Promise.map(addresses, processSingleAddress);
};
var processSingleAddress = function (address) {
// How you implement this is up to you -- and up to whatever
// promise library you are using. return a promise that resolves
// to the value you want
};
Here, we are using map, but we could use reduce. There are some extremely important performance characteristics between map and reduce (see below), but I will ignore that right now. All you need to know is that you are returning a promise that will resolve or reject when all of the items in addresses have been processed by the callback, processSingleAddress.
The question is how to write processSingleAddress.
If you can process that single address using in a way that already uses promises, then it is simple. Maybe you are using a library that is already Promise-ready. In this case, you just call that function directly.
var processSingleAddress = function (address) {
return addressProcessorWithPromise(address);
};
But if you cannot process your list items with promises, you need to figure out a way to do so using promises you create yourself. Here, for example, I am assuming that you are making an asynchronous call to getDataFromAsyncProcess() and that function takes a single address and a node-style callback.
var processSingleAddress = function (address) {
// input is `sample#example.com`
return new Promise(function (resolve, reject) {
getDataFromAsyncProcess(address, function (err, data) {
if (err) reject(err);
// example output is {address: 'sample#example.com', status: 'logged in'}
resolve(data);
});
});
};
At this point, you should expect things to run like this:
var addresses = ['sample1#example.com', 'sample2#example.com']
processEmailAddressList(addresses)
.then(function (list) {
// see below for the value of `list`
})
.catch(function (err) {
console.error(err);
});
Inside then, list should now look like this:
[
{address: 'sample1#example.com', status: 'logged in'},
{address: 'samples#example.com', status: 'logged in'},
]
I know this looks potentially confusing with the amount of indirection that appears to be going on (functions inside functions), but welcome to JS. And the more you get used to using promises, the easier it gets.
THe last thing to be really careful about is how map or reduce (or any other promise-library iterator) does things. If you have, 5 addresses and the process is not so expensive, this is not so critical, but if you have hundreds or thousands of things to process or the processor is expensive (database calls, network requests, memory consumption, etc.), you can end up chewing up a ton of memory and killing your database if all of those addresses are processed in parallel (all at once). If this is a factor, you should try to use an iterator that does things one at a time. Consult your library API for details.
Last note: please try to keep your questions simple. That was unnecessarily verbose.
I am trying to call a loopback find function inside of a for loop, passing in a value from the iteration into the loopback function. The main issue of the code can be represented by the following:
for (var a = 0; a < $scope.countries.length; a++) {
$scope.getEmFacPurElec($scope.countries[a], 'ton/kWh', 'CO2e').then(function(result) {
emFacPurElecToUse = $scope.emFacPurElecs;
}
And here is the function being called:
$scope.getEmFacPurElec = function (country, unit, ghgType) {
var defer = $q.defer();
$scope.emFacPurElecs = [];
$scope.emFacPurElecs = Country.emFacPurElecs({
id: country.id,
filter: {
where: {
and: [
{unit: unit},
{ghgType: ghgType}
]
}
}
});
defer.resolve('Success getEmFacPurElec');
return defer.promise;
};
The problem is that the loopback promise function is called and then returned undefined which means that it moves to the next iteration of the for loop before getting the value to assign to emFacPurElecToUse. I need to do some more calculations with that variable for that country before moving to the next country.
I have looked at using $q.all as a possible solution and also using array.map as per http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html (Rookie mistake #2: WTF, how do I use forEach() with promises?), but I just cannot figure out how to pull it all together to make it work. Should I be using a forEach instead?
I also saw this link angular $q, How to chain multiple promises within and after a for-loop (along with other similar ones) but I do not have multiple promises that I need to process inside the for loop. I need to retrieve the value of one emFacPurElecs for that country, do some work with it, then move to the next country. I feel I am close but I just cannot get my head around how I would code this particular functionality. Any help is greatly appreciated.
It seems to me that you do have multiple promises to process inside your for loop, as you say "I need to do some more calculations with that variable for that country before moving to the next country." This should all be done with in the promise chain that I've suggested - calcEmFacPurElec.
$scope.calcEmFacPurElec = function (country, unit, ghgType) {
$scope.getEmFacPurElec(country, unit, ghgType).then(function(countryEmFacPurElecs) {
// do something with countryEmFacPurElecs
return countryEmFacPurElecs;
}
$scope.getEmFacPurElec = function (country, unit, ghgType) {
var defer = $q.defer();
defer.resolve(Country.emFacPurElecs({
id: country.id,
filter: {
where: {
and: [
{unit: unit},
{ghgType: ghgType}
]
}
}
}); );
return defer.promise;
};
Hopefully the above is a pointer in the right direction!
When you want to carry out a promise chain on an array of items, then as you have identified, Promise.all (using whatever promises implementation you require) is what you want. .all takes in an array of Promises, so in your for loop you can do:
var promises = [];
for (var a = 0; a < $scope.countries.length; a++) {
promises.push($scope.calcEmFacPurElec($scope.countries[a], 'ton/kWh', 'CO2e')); // new promise chain that does all of the work for that country
}
$q.all(promises).then(function(arrayofCountryEmFacPurElecs) {console.log('all countries completed')});
I am trying to call urls one AFTER another, not all at the same time, but no matter what I seem to try, they seem to all happen at the same time. This is what I have now...
$http.get('/some/url/foo1')
.then($http.get('/some/url/foo2'))
.then($http.get('/some/url/foo3'))
.then($http.get('/some/url/foo4'))
.then($http.get('/some/url/foo5'))
.then($http.get('/some/url/foo6'))
.then($http.get('/some/url/foo7'))
.then($http.get('/some/url/foo8'))
.then($http.get('/some/url/foo9'))
.then($http.get('/some/url/foo10'))
.then($http.get('/some/url/foo11'))
.then($http.get('/some/url/foo12'))
.then($http.get('/some/url/foo13'))
.then($http.get('/some/url/foo14'));
These can't happen at the same time, when one completes, I want the next one to start.
EDIT: I've also tried putting the get in a function like this, but they still all get called at the same time
$http.get('/some/url/foo1')
.then(rebuildModel('foo2'))
.then(rebuildModel('foo3'))
.then(rebuildModel('foo4'))
.then(rebuildModel('foo5'))
.then(rebuildModel('foo6'))
.then(rebuildModel('foo7'))
.then(rebuildModel('foo8'))
.then(rebuildModel('foo9'))
.then(rebuildModel('foo10'))
.then(rebuildModel('foo11'))
.then(rebuildModel('foo12'))
.then(rebuildModel('foo13'))
.then(rebuildModel('foo14'));
function rebuildModel(modelName) {
return $http.get('/some/url/' + modelName);
}
Edit2: This worked...I see what I did wrong
function rebuildModel(modelName) {
return function () {
return $http.get('/some/url/' + modelName);
}
}
The then method expects a success callback function as its first parameter:
$http.get('url').then(successCallback);
The successCallback must be a function definition, such as:
$http.get('url').then(function() { ... });
If you provide $http.get() as an argument:
$http.get('url').then($http.get('url'));
You are calling a function, and then passing the return value (which is a promise object) to the then method as the successCallback.
The situation is analogous to the following more obvious scenario:
a. alert
b. alert()
The first is a function definition, the second is calling the function.
I agree with Chandermani's answer, and it should be correct. If it is not working, perhaps check for errors:
$http.get('/some/url/foo1')
.then(function() { return $http.get('/some/url/foo2'); })
.then(function() { return $http.get('/some/url/foo3');},
function() { alert('something went wrong');});
The chaining that you have done is incorrect. It should be something like this:
$http.get('/some/url/foo1')
.then(function() { return $http.get('/some/url/foo2'); })
.then(function() { return $http.get('/some/url/foo3');})
Remember the then function too returns a promise, that is resolved by the return value of its, success and error callbacks.
I want to get all names of files and directories from path and recognize them as files and directories. but When i run my code sometimes it works and somentimes it shows that directories are files. Here is the code
socket.on('data',function(path){
fs.readdir('path',function(err, data) {
var filestatus=[];
var z=0;
var i=data.length-1;
data.forEach(function(file){
fs.stat(file, function(err, stats) {
filestatus[z]=stats.isDirectory()
if (z==i){
socket.emit('backinfo',{names:data,status:filestatus});
}
z++;
})
})
})
})
During tests i realized that when i slow down data.forEach loop (using console.log(something) it works better(less miss). And this is strange.
This is about 96% incorrect, thank you to JohnnyHK for pointing out my mistake, see the comments below for the real problem / solution.
Because the fs.stat() function call is asynchronous, the operations on the filestatus array are overlapping. You should either use the async library as elmigranto suggested, or switch to using fs.statSync.
More details on what's happening:
When you call fs.stat(), it basically runs in the background and then immediately goes onto the next line of code. When it has got the details of the file, it then calls the callback function, which in your code is the function where you add the information to the filestatus array.
Because fs.stat() doesn't wait before returning, your program is going through the data array very quickly, and mutliple callbacks are being run simultanously and causing issues because the z variable isn't being incremented straight away, so
filestatus[z]=stats.isDirectory()
could be executed multiple times by different callbacks before z gets incremented.
Hope that makes sense!
you are using for statement in NODEJS and this will work if turned the For Statement to recursive function please see the attached code for help
function load_Files(pat,callback) {
console.log("Searching Path is: "+ph);
fs.readdir(pat,(err,files)=>
{
if(err)
{
callback(err);
}
else
{
var onlydir=[];
var onlyfiles=[];
var d=(index)=>
{
if (index==files.length)
{
console.log("last index: "+ index);
var ar=[];
ar.concat(onlydir,onlyfiles);
callback(null,ar);
return;
}
fs.stat(files[index],(err,status)=>
{
console.log("the needed file " +files[index]);
if (status.isDirectory())
{
onlydir.push(files[index]);
}
else
{
onlyfiles.push(files[index]);
}
console.log("only Directory: "+onlydir.length);
console.log("index: "+ index);
d(index+1);
}
)
}
d(0);
}
});
}