In my controller with the help of service i'm sending some qr-data to users so:
$scope.sendtoList = function () {
$scope.qrStatus = false;
angular.forEach($scope.users, function (item) {
if (item.Selected){
inviteService.sendQR(item.Emails.main, $scope.company.Id).then(function(response) {
$scope.qrStatus = true;
},
function(err) {
$scope.qrStatus = false;
});
}
});
if ($scope.qrStatus){
$window.alert('QR-code has been sended successfully.');
}
else{
$window.alert('Warning! QR-code has not been sended successfully.');
}
}
and i see some strange behaviour: it always show warning alert, even if method is done succesfully - i think it is of promisses. But how could i show window in my case only after servise promisse is returned?
In order to do this with promises, you need to create a counter that checks every time a user is updated, and then triggers the alert once all are accounted for.
$scope.sendQRtoList = function () {
$scope.qrStatus = false;
var count = 0;
var length = $scope.users.length;
var error = 0;
angular.forEach($scope.users, function (item) {
if (item.Selected){
inviteService
.sendQR(item.Emails.main, $scope.company.Id)
.then(function(response) {
//yay!
},
function(err) {
error++;
})
.finally(function () {
count++;
if (count === length && error === 0) {
$window.alert('QR-code has been sent successfully.');
}
if (count === length && error !== 0) {
$window.alert('Warning! QR-code has not been sent successfully.')
}
});
}
});
};
.finally() happens on every promise, that's where you want to add your counter incrementation.
Hope one of the following helps
1) Try looking at the following post: $success call back function from AngularJS
Its a similar question and the following js fiddle might help: http://jsfiddle.net/E5HGy/6/
2) A counter as stated above would also solve the problem, something such as
if(i == selector.length)
// "callback"
would essientially solve it
Related
I've got a task where I need to return 10 Chuck Norris jokes from an API. This part I got right but the second part (Get a random joke every 5 seconds) is breaking me a bit.
// Get jokes by amount
getJokes(amount) {
const jokeArray = [];
const randomJokeArray = [];
if(amount > 1){
this.http.get(http://api.icndb.com/jokes/random/' + amount).subscribe(data => {
// Test to see if call was successful
if(data["type"] === 'success'){
for(var i = 0; i <= data["value"].length; i++){
jokeArray.push(data["value"][i]);
}
} else {
console.warn("API Call 'getJokes' was unsuccessful");
}
});
} else {
this.http.get('http://api.icndb.com/jokes/random/' + amount).subscribe(data => {
// Test to see if call was successful
if(data["type"] === 'success'){
randomJokeArray = data["value"][0];
console.log(randomJokeArray);
} else {
console.warn("API Call 'getJokes' was unsuccessful");
}
});
}
if(amount > 1){
// Return the jokeArray
return of (jokeArray);
} else {
return of (randomJokeArray);
}
}
randomJokeArray always comes back as undefined.
This function is inside of a service and me being fairly new to TypeScript and Angular. I'm pretty sure I'm using it wrong. Can anyone please show me how I can return only one joke from the API?
If I duplicate the API call of 10 that works I get the following returned when only calling for 1.
From the main file
this.configService.getJokes(1).subscribe(j => this.randomJokeArray = j);
From the service file
for(var i = 0; i <= data["value"].length; i++){
randomJokeArray.push(data["value"][i]);
}
return of (randomJokeArray);
You have three issues in your code
Your component is subscribing to a Subscription not an Observable. So you should map on the response returned by http instead of subscribing, because you wish to return Observable to your component.
this.http.get('http://api.icndb.com/jokes/random/' + amount).pipe(map(data => {
// Test to see if call was successful
.........................................
}));
Main issue : You are not waiting for the asynchronous execution to be completed.
Move your if(amount > 1){...} code inside http.get.map()
return this.http.get('http://api.icndb.com/jokes/random/' + amount).pipe(map(data => {
// Test to see if call was successful
if(data["type"] === 'success'){
randomJokeArray = data["value"][0];
console.log(randomJokeArray);
} else {
console.warn("API Call 'getJokes' was unsuccessful");
}
if(amount > 1){
// Return the jokeArray
return of (jokeArray);
} else {
return of (randomJokeArray);
}
}));
add return statement before http.get.map()
return this.http.get('http://api.icndb.com/jokes/random/' + amount).pipe(map(data => {
// Test to see if call was successful
.........................................
}));
This is my first time here, and I am really lost on what to do.
So I have a loop where I am making a post request to an API. I need to send some questions and get their rates match back. After that, I need to calculate the highest match and set an answer in some $scope variable to display on the screen.
The problem I have is that, since the call is asynchronous, sometimes it will display the wrong answer (because it's the last response returned).
I know it's asynchronous and I should have some callback, but I tried many ways and still can't figure out how to. All I want is to be able to "sort" the result so I can get the max number and display the associated answer AFTER all the calls are done. What I have so far is that:
for (var i = 0; i < $scope.possibleQuestions.length; i++) {
var compare = compareAPI($scope.results, $scope.possibleQuestions[i].question,
$scope.possibleQuestions[i].answer,
$scope.possibleQuestions[i].keywords,
$scope.possibleQuestions[i].similar,
function (score, question, answer, keywords, similar) {
$scope.compareAPI = score.average;
if ($scope.compareAPI >= 0.6) {
realAnswer = answer;
questionsAskedCount++;
$scope.answer = realAnswer;
} else {
var isMatch = matchKeywordAPI(question, keywords);
if (isMatch == 0) {
$scope.answer = "Please try asking another question!";
}
else {
//have to return a string just to know, bcause realAnswer is undefined in here, have to return callback function hahahahaha again, or set the answer in the match function
$scope.answer = realAnswer;
}
}
});
}
And the other function:
function compareAPI (possibleQuestion, possibleAnswer, fn) {
console.log("compare API");
var apiMatch = semanticService.getSemantic($scope.studentQuestion, possibleQuestion)
apiMatch.then(function (result) {
fn(result.data, possibleQuestion, possibleAnswer);
console.log(result.data);
}, function(error){
$scope.status = 'Unable to load question data: ' + error.message;
});
}
My biggest problem is that this part
if ($scope.compareAPI >= 0.6) {
realAnswer = answer;
questionsAskedCount++;
$scope.answer = realAnswer;
} else {
var isMatch = matchKeywordAPI(question, keywords);
if (isMatch == 0) {
$scope.answer = "Please try asking another question!";
}
else {
$scope.answer = realAnswer;
}
}
is random because of the async, so if the wrong answer is the last response, it will go to the 'else' and the answer gets wrong.
Any help will be very appreciated!!! Thanks!
Promises can done sequentially by chaining them:
var promise = $q.when(null);
for (let i=0; i<promiseArray.length; i++) {
promise = promise.then(function() {
//return promise to chain
return promiseArray[i];
});
};
promise.then(function() {
console.log("ALL Promises Done");
});
For more information, see AngularJS $q Service API Reference - Chaining Promises.
I am a beginner in javascript, and I'm trying to figure out why my while loop won't actually loop more than once, even though the condition is always met.
I have a function sending an API request:
var get_status = function(trid, count) {
console.log(count);
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
$http(req).success(function(data) {
if (data.transaction_status != 'Pending') {
// do something with the data
console.log('true');
return true;
}
else {
console.log('False');
return false;
}
}).error(function(data) {
// show an error popup
console.log('true');
return true;
})
}
};
I want to call this function until it returns true, so I call it this way:
var count = 0;
while (get_status(id, count) === false) {
count += 1;
}
The count variable is just added to see how many times it loops, it stays at 0 even though 'False' is displayed in the console.
Is there some behaviour I am misunderstanding here?
EDIT I understand why this won't work. My intention here is to display an iframe as long as the transaction status is pending. I thought of continually sending a request until the transaction status is something other then 'Pending', but I am aware there are more optimal ways.
Your get_status() function does not return a value. Thus, it's return value is undefined which is falsey so your while() loop stops after the very first iteration.
The return statements you do have in your code are inside of callbacks and have nothing to do with the return value of get_status().
What you are attempting to do is generally not a good design. It appears that you want to run a given Ajax call over and over with no delay until you get the answer you want. This will potentially hammer the destination server.
If you describe the problem you're really trying to solve, we could help come up with a better way to do this. Worst case, you could poll the server with a time delay between requests.
If you wanted to poll every so often, you could do something like this:
function get_status(trid, count) {
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
return $http(req).then(function(data) {
return data.transaction_status;
});
}
function poll_status(callback) {
function next() {
get_status(...).then(function(status) {
if (status === "Pending") {
// poll once every two seconds
setTimeout(next, 2000);
} else {
// status is no longer pending, so call the callback and pass it the status
callback(status);
}
}, function(err) {
callback(err);
});
}
next();
}
poll_status(function(result) {
// done polling here, status no longer Pending
});
This is not the correct way to deals with async calls, I'd create a recursive function which will call itself. (in this case get_status should return a promise)
Code
var count = 0, id = 1;//id should be some value
(function myCall(promise){}
promise.then(function(data){
count += 1;
if(data)
myCall(get_status(id, count)); //call function on conditon
});
}(get_status(id, count))
Method(Returning Promise)
var get_status = function(trid, count) {
console.log(count);
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
//returning promise here
return $http(req).then(function(response) {
var data = response.data;
if (data.transaction_status != 'Pending') {
// do something with the data
console.log('true');
return true; //resolves the promise
}
else {
console.log('False');
return false; //resolves the promise
}
}, function(data) {
// show an error popup
console.log('true');
return true;
})
}
};
You're trying to return from within an asynchronous callback, which won't work, unfortunately. Instead you'll want a module like async, specifically whilst.
var count = 0;
var outcome = false;
async.whilst(
function () { outcome = false; },
function (callback) {
count++;
// Your code here, setting outcome instead of returning
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
$http(req).success(function(data) {
if (data.transaction_status != 'Pending') {
outcome = true;
callback();
}
else {
outcome = false
callback();
}
}).error(function(data) {
outcome = true;
callback();
})
},
function (err) {
// All done!
}
);
But really the behavior you're looking for is probably checking on a status at pre-defined intervals. In this case, adapting the code
var count = 0;
var outcome = false;
async.whilst(
function () { outcome = false; },
function (callback) {
count++;
// Your request stuff.
setTimeout(function () {
callback();
}, 1000); // Waits one second to begin next request
},
function (err) {
// All done!
}
);
After a succesful query to Mongodb to get a list of news, for the news that have a link attached i search for link details in the database but after i set them inside the modified news object i cannot push it to an array. Why does this happen?
var newsarray = []
for(i=0;i<newsfound.length;i++){
if(!newsfound[i].link._id){
newsarray.push(newsfound[i])
} else {
var tempnew = newsfound[i];
db.findOne('links',{'_id':tempnew.link._id},function(err,linkdetails){
if(err){
console.log(err)
} else {
tempnew.linkdetails = linkdetails;
newsarray.push(tempnew)
}
})
}
}
console.log(newsarray)
The result of this is an array without the link containing news or if i try a few changes an array with the original news without the added details.
You can not use an asynchronous function inside a for loop. The reason is that the for loop gets executed before even any callback could come, and hence the scope gets messed up.
You should use recursive function (self calling) which will prevent the next async call before the previous is over.
var i = 0;
function fn() {
// your logic goes here,
// make an async function call or whatever. I have shown async in a timeout.
setTimeout(function () {
i += 1;
if (i < newsfound.length) {
fn();
} else {
// done
// print result
}
});
}
Update:
For your use case,
var newsarray = []
var i = 0;
function done() {
// done, use the result
console.log(newsarray);
}
function fn() {
if (!newsfound[i].link._id) {
newsarray.push(newsfound[i]);
i += 1;
if (i < newsfound.length) {
fn();
} else {
done();
}
} else {
var tempnew = newsfound[i];
db.findOne('links', {'_id':tempnew.link._id}, function(err, linkdetails){
if(err){
console.log(err)
} else {
tempnew.linkdetails = linkdetails;
newsarray.push(tempnew);
i += 1;
if (i < newsfound.length) {
fn();
} else {
done();
}
}
})
}
}
if (option == 'Follow All') {
for (var i = 0; i < userArray.length; i++) {
followUser(params..);
}
// How to get this part to execute after followUser is done? (Basically when the for loop finishes)
alert("There was a problem processing your request on Twitter to follow the following users: " + $('#errored-users').val());
$('#errored-users').val('');
}
How can I call this first multiple times and wait for it to finish?
var followUser = function(params..) {
$.post('/api/1.0/followUser.php', {
'user_to_follow': username,
'username': user
}).done(function(data) { {
if (!is_batch) {
alert("There was a problem processing your request on Twitter to follow #" + username);
} else {
//This currently gets executed last?
var names = $('#errored-users').val();
if (names == "") {
names = '#' + username + ', ';
} else {
names += '#' + username + ', ';
}
$('#errored-users').val(names);
}
};
Since you are already using jQuery, you can easily use the AJAX requests/promises and wait for all of them to complete. $.when can help you a lot with this:
var followUser = function(params..) {
// return the promise!
return $.post('/api/1.0/followUser.php', { ... });
};
if (option == 'Follow All') {
var promises = [];
for (var i = 0; i < userArray.length; i++) {
promises.push(followUser(...));
}
$.when.apply(null, promises)
.done(function() {
// all users were successfully followed
})
.fail(function() {
// at least one follow failed; no information about the others
alert("There was a problem processing your request...");
$('#errored-users').val('');
});
}
This will call the .done handler when all requests have completed, but it will call the .fail handler as soon as just one has failed.
If instead you want some handler to run after all requests have completed (either success or failure) you 'd need to do it more manually, for example:
var followUser = function(params..) {
// return the promise!
return $.post('/api/1.0/followUser.php', { ... });
};
if (option == 'Follow All') {
var outcomes = { done: [], failed: [] };
var total = userArray.length;
function allFinished() {
return outcomes.done.length + outcomes.failed.length == total;
}
for (var i = 0; i < total; i++) {
followUser(...)
.done(function() {
outcomes.done.push(username);
})
.fail(function() {
outcomes.failed.push(username);
})
// this must come last
.always(function() {
if (allFinished) {
// outcomes contains the results
}
})
}
}
This will still use jQuery's notion of a request having succeeded or failed, which is based on Twitter's HTTP response codes. If you want to customize this behavior you can amend followUser as such:
var followUser = function(params..) {
return $.post('/api/1.0/followUser.php', { ... })
.then(
// first argument handles HTTP response successes, but you can
// convert them to failures here:
function(data) {
if (convertSuccessToFailure) {
return $.Deferred.reject(data);
}
});
};
As of jQuery 1.5 any of the $.ajax family of functions return a promise - and you can combine multiple promises into a new promise that will be resolved when all the child promises are resolved using $.when:
function followUser(/* params */) {
return $.post('/api/1.0/followUser.php', {
user_to_follow: username,
username: user
});
}
var userRequests = [];
for (var i = 0, l = userArray.length; i < l; i++) {
userRequests.push(followUser(/* params */));
}
$.when.apply($, userRequests).then(function(data) { /* etc. */ });
You could define a global variable which holds the number of calls to followUser:
if (option == 'Follow All') {
var countUsers = userArray.length;
for (var i = 0; i < countUsers; i++) {
followUser(params..);
}
}
Then you change the anonymous function to count backwards and execute your last statement if all users are done:
function(data) {
if (!is_batch) {
alert("There was a problem processing your request on Twitter to follow #" + username);
} else {
(...)
}
countUsers--;
if(countUsers == 0){
alert("There was a problem processing your request on Twitter to follow the following users: " + $('#errored-users').val());
$('#errored-users').val('');
}
};
A potential solution for this is to use Promises (see here for an in-depth explanation). It provides a new style of coding in Javascript that effectively enables you to make asynchronous code synchronous. (This is a big simplification of Promises - see here for an article explaining it a little bit more).
There are various implementations which you could use. The one I most use is found here: https://github.com/cujojs/when. The examples provided within it's wiki demonstrates the power of promises (see here).
The basic outline for your code using when.js would look and read something like this:
if (option == 'Follow All') {
var deferreds = [];
for (var i = 0; i < userArray.length; i++) {
deferreds.push(followUser(params..));
}
when.all(deferreds).then(function everythingWasFine(suceededUsernames) {
//do something with the responses e.g.
alert(succeededUsernames.length + ' users were followed');
},
function somethingWentWrong(failedUsernames) {
alert("There was a problem processing your request on Twitter to follow the following users: " + failedUsernames.join(','));
});
}
var followUser = function(params..) {
var defer = when.defer();
$.post('/api/1.0/followUser.php', {
'user_to_follow': username,
'username': user
}).done(function(data) {
if (failure) {
defer.reject(username);
} else {
defer.resolve(username);
}
});
return when.promise;
};