angular js combine asynchronous and synchronous code - javascript

I am using angular resource modul to work with my restfull webserver. For exampe i have a method which returns an array of 10 elements and what i want to do is just save result into some javascript variable (not in angular scope) called books.
So i have written a simple method which looks like this:
function getBooks(user_id) {
var books = [];
BookFactory.getBooks.query({ id: user_id }).$promise.then(function (result) {
angular.forEach(result, function(i) {
books.push(i);
});
});
return books;
}
Let's assume that BookFactory.getBooks.query works as expected and truly returns 10 elements.
So there is a simple logic inside this function, i just push each element to the array books.
Also i have a test function for testing getBooks() function. Here it is:
$scope.testGetBooksMethod = function (user_id) {
var resut = getBooks(user_id);
alert(resut.length);
};
The result in alert will be always 0. I know that this part of code:
BookFactory.getBooks.query({ id: user_id }).$promise.then(function (result) {
angular.forEach(result, function(i) {
books.push(i);
});
});
works asynchronously and until server request is processing, function getBooks() returns an empty array of books (please correct me if i am wrong).
Here is a question, how can i edit my function, to work it correctly. I want to get data from rest, fill the array books with this data and then return it.
Thanks in advance.

I want to get data from rest, fill the array books with this data and then return it.
This is the thing. Asynchronous programming is not really compatible with synchronous. So you can't just combine them. However you could work in a synchronous-like way, and this is where promises comes very powerful. You don't return data, you return stateful Promise object instead.
So instead of returning books return a Promise for this books:
function getBooks(user_id) {
return BookFactory.getBooks.query({ id: user_id }).$promise;
}
Then in consuming code you would use this wrapper object with underlying state control mechanism which will proceed when books are loaded and available:
$scope.testGetBooksMethod = function (user_id) {
getBooks(user_id).then(function(books) {
alert(books.length);
});
};

You need to use promise concept here, testGetBooksMethod of controller function will wait till getBooks of service method will complete its call. For that you need to return the BookFactory.getBooks.query promise from getBooks function. After data gets retrieve the books are will get created & will return from the getBooks method. While calling getBooks method you need to use .then function that will continue the chain promise thing, when data is returned from the getBooks this then function will get the data returned from the your service.
function getBooks(user_id) {
var books = [];
return BookFactory.getBooks.query({ id: user_id }).$promise.then(function (result) {
angular.forEach(result, function(i) {
books.push(i);
});
return books;
});
}
Controller
$scope.testGetBooksMethod = function (user_id) {
getBooks(user_id).then(function(resut){
alert(resut.length);
});
};

Related

JavaScript multiple promise issue with $.get

function getHiddenTable(){
var url = ".......";
var apptoken = "blahblah";
return $.get(url, {
act: "API_DoQuery",
clist: "7.8.6",
data: apptoken
});
}
function getRprTable(){
var url = "........";
var apptoken = "blahblah";
return $.get(url, {
act: "API_DoQuery",
clist: "3.60",
data: apptoken
});
}
function main(){
getHiddenTable().then(function(xml_hidden){
hiddenTable = xml_hidden;
console.dirxml(hiddenTable);
return getRprTable();
}).then(function(xml_rpr){
rprTable = xml_rpr;
console.dirxml(rprTable);
});
}
main();
getHiddenTable() returns a $.get request and getRprTable() also returns a $.get request
Both of these functions return two separate xml files that contain different data. I assign both xml files to two separate global variables so they have scope to other function that I need to use them in. However, both console statements display the initial request from getHiddenTable(). But why? And what can I do to fix this?
Update
Hey guys, thanks for the help. I found out my problem with much frustration and actually utilized $.when, from the person who provided the idea in the comments.
$.when(getHiddenTable(), getRprTable()).done(function(xml_hidden, xml_rpr){
console.dirxml(xml_hidden[0]);
console.dirxml(xml_rpr[0]);
});
Accessing the data value of the callback function objects can be done by simply accessing the 0th index.
Thanks
You'll likely want to use Promise.all() to run multiple promises at once and return the results of all the promises once they have all finished.
Example:
Promise.all([getHiddenTable(), getRprTable()])
.then(function(results) {
let hiddenTable = results[0];
let rprTable = results[1];
// do stuff with your results
});
Hope that helps

How to wrap an async package function?

I'm using the popular node library, got, to make simple GET requests to a JSON API.
I have a function that abstracts the request, like so:
function performRequest(url) {
got(url, {
json: true
}).then(function (response) {
return formatResponse(response.body);
}).catch(function (error) {
console.log(error.response.body);
});
}
formatResponse is a simple synchronous method that modifies the JSON returned from the API.
I would like to be able to call performRequest from another function and then use the return value (once resolved). Currently, as performRequest is not recognized as an async method, my code is calling it and then proceeding immediately.
function myBigFunction() {
var url = composeUrl();
var res = performRequest(url);
doMoreStuffWithResponse(res);
}
I know that I need to utilize a Promise, however, I'm always unclear as to how to use a Promise in conjunction with a built-in library function that is already using a Promise (like in this case).
I'm also completely open to the possibility that I'm going about this all wrong. In that case, I would appreciate some redirection.
Thank you for your time.
Understand what a Promise is. Its a value, you can treat it as such. In order to "read" the value, you pass a function to the Promise's then method. You don't need myBigFunction. Anything you want to run after the Promise resolves just needs to be passed to then:
var req = performRequest(composeURL());
req.then(doStuffWithResponse);
Now, I don't particularly care for this way although I do it fairly often. I prefer to have functions that take promises and invoke their then method:
var takesAPromise = function(p) {
return p.then(/* does stuff */);
};
Note that it returns the Promise of the completed task. But what I like even better is this ES6 one-liner:
let wrap = f => p => p.then(val => f.call(null, val));
Now you can wrap arbitrary functions to take Promises as input and return them as output. If Promises were a monad, this would be their bind function. Making it work seamlessly with functions of arbitrary arity is left as an exercise to the reader.
You'll always want to return a promise from your functions:
function performRequest(url) {
return got(url, {
//^^^^^^
json: true
}).then(function(response) {
return formatResponse(response.body);
}, function(error) {
throw new Error(error.response.body);
});
}
With this, you can wait for the result in your big functions using another then:
function myBigFunction() {
var url = composeUrl();
var promise = performRequest(url);
return promise.then(function(res) {
return doMoreStuffWithResponse(res);
});
}
or in short
function myBigFunction() {
return performRequest(composeUrl()).then(doMoreStuffWithResponse);
}
so that you can call it like
myBigFunction().catch(function(error) {
console.log(error.message);
});

Promises with angularJS and Typescript

I have the following function, my goal is to push to the items list, when the components can identify their parent item.
The problem that I have is that when I am pushing to the list console.log() shows me that the object is in there, but when I return the list and catch it in another function, there is nothing in the list.
I think that the items list is returned before the code above it is done.
private get_items_for_request() {
return this.Item.forRequest(this.request.id, ['group'])
.then((_items) => {
var items = [];
for (var item of _items) {
return this.ItemComponent.forItem(item.id, ['type'])
.then((_components) => {
for (var component of _components) {
if (component.type.can_identify_item) {
items.push({
group_id: item.group.reference,
identifier_code: this.remove_check_digit_if_necessary(
component.identifier_code),
quantity: 1
});
break;
}
}
}, (reason) => {
this.Toast.error(
this.gettextCatalog.getString('components.load_failed'));
return [];
});
}
return items;
}, (reason) => {
this.Toast.error(
this.gettextCatalog.getString('items.failed_load'));
return [];
});
}
I'm afraid the trouble is with your approach, not with the code itself. A promise is just that - a promise of data some time in future. So, when you function returns (immediately), the promise is not resolved yet and the data your caller function captures is still empty. I don't see console.log() in your code, but would suspect you put it somewhere inside then(), which will be called once data has been received. So you will see data being logged. Trouble is by that time get_items_for_request() has already returned and your caller function has already moved on.
If you want to use a promise, you have to use a callback to be called once promise is resolved. But if you want actual data returned to the caller you need to fetch data synchronously.
For the synchronous fetch, check this response. But beware that synchronous fetch will hurt responsiveness of your script.
For an asynchronous fetch (using promises), you need to define a callback called once all data has been fetched. I won't try to fix your code, but it should go along the following sketch in javascript. Though keep in mind it's just a sketch.
function get_items_for_request(onSuccess) {
var items = []
var total = -1
var sofar = 0;
this.Item.forRequest(this.request.id, ['group'])
.then(function (_items) {
var items = [];
total = _items.length // remember how many nested calls will be made
for (var item of _items) {
this.ItemComponent.forItem(item.id, ['type'])
.then(function (_components) {
// push received data to the items array here
sofar++
if (sofar == total) { // all done
onSuccess(items)
}
}
}
}
}
In the first promise callback, you have two returns, only the first one runs. You return a promise after the first for, which you resolve to undefined. You should wait for an array of promises, each corresponding to a this.ItemComponent.forItem call.
Promise.all helps with this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
You should do something like this:
return this.Item.forRequest(this.request.id, ['group']).then((_items) => {
return Promise.all(_items.map(function (item) {
return this.ItemComponent.forItem(item.id, ['type']).then((_components) => {
for (var component of _components) {
if (component.type.can_identify_item) {
return {
group_id: item.group.reference,
identifier_code: this.remove_check_digit_if_necessary(
component.identifier_code),
quantity: 1
};
}
}
}, (reason) => {
this.Toast.error(
this.gettextCatalog.getString('components.load_failed'));
return [];
});
}));
}, (reason) => {
this.Toast.error(
this.gettextCatalog.getString('items.failed_load'));
return [];
} )
If you only want one item, find the first non-falsy element in the resulting array

How to make queries in loop using Parse promises in cloud code?

The response received is always an empty array. The inner stands_query in the for loop never gets executed. Also I would like to know if there is again an inner query inside the stands query then how do I achieve that.
STANDS CLASS STADIUMS CLASS Code below :
var final_list = [];
query.find().then(function(stadiums){
_.each(stadiums,function(stadium){
var stands_query = new Parse.Query(“Stands");
stands_query.equalTo(“stdId”,stadium.get(“stdId"));
var promise = stands_query.find().then(function(stands){
_.each(stands,function(stand){
var jsonObject = {
“stdId": stand.get(“stdId").id,
}
final_list.push(jsonObject);
});
return null;
},function(error){
return response.error(error);
});
return promise;
});
}).then(function(){
response.success(final_list);
});
Your first .then isn't returning anything. I'll break your code down so you can see it:
query.find().then(function(stadiums){ //anonymous function 1
_.each(stadiums,function(stadium){ //anonymous function 2
return "foo" //this returns "foo" as a result of anonymous function 2.
});
//Nothing explicitly returned from function 1!
}).then(function(){
response.success(final_list);
});
A function that lacks an explicit return statement will return undefined. Your code then executes "response.success" before any of the internal promises resolve.
What you could do instead is create an array of internal promises that you wait for with Parse.Promise.when:
query.find().then(function(stadiums){
var promises = [];
_.each(stadiums,function(stadium){
var promise = stands_query.find().then(...)
promises.push(promise);
});
//if returning another promise, the ".then" won't execute until it completes.
return Parse.Promise.when(promises);
}).then(function(){
response.success(final_list);
});
All this being said, you may run into timeout issues depending on how large your dataset is. Consider rewriting your query so that you query for Stands belonging to a Stadium with relational queries instead.
Update
Now that you've updated your question with fields, it looks like your line stands_query.equalTo(“stdId”,stadium.get(“stdId")); has two mistakes and will never return results. It should be stands_query.equalTo(“stadiumId”,stadium);.
We have many Stadiums, and every stadium has many Stands. The relationship between Stadium and Stand is represented in data by a pointer column on the Stands class called "stadiumId".
In comments, the functional goal is stated very simply: a JSON array of stands. This requires a single query, no looping at all:
function allTheStands() {
var query = new Parse.Query("Stands");
query.include("stadiumId");
return query.find().then(function(stands) {
return JSON.stringify(stands);
});
}
// call it like this:
allTheStands().then(function(jsonStands) {
// jsonStands is all of the stands represented as son
});
EDIT
A more roundabout way to the same result is to not include stadiumId in the query, instead doing a fetch after the stands query completes.
(This is just a specific form of advice given by #adamdport, given details of your data. You should credit his answer if you find this useful).
// adding underscorejs for handling arrays and other utils
var _ = require('underscore');
function allTheStands() {
var stands;
var query = new Parse.Query("Stands");
return query.find().then(function(result) {
stands = result;
// we're not done yet, because we need to fetch each stand's stadium
var promises = _.map(stands, function(stand) {
return stand.get("stadiumId").fetch().then(function(stadium) {
stand.set("stadiumId", stadium);
});
});
// as adamdport suggests, the crux of the looping answer is to use Promise.when()
return Parse.Promise.when(promises);
}).then(function() {
return JSON.stringify(stands);
});
}

How to wait for AsyncRequests for each element from an array using reduce and promises

I'm new on AngularJS and JavaScript.
I am getting remote information for each of the elements of an array (cars) and creating a new array (interested prospects). So I need to sync the requests. I need the responses of each request to be added in the new array in the same order of the cars.
I did it first in with a for:
for (a in cars) {
//async request
.then(function () {
//update the new array
});
}
This make all the requests but naturally didn't update the new array.
After seeking in forums, I found this great examples and explanations for returning a intermediate promise and sync all of them.
1. http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
2. http://stackoverflow.com/questions/25605215/return-a-promise-from-inside-a-for-loop
3. http://www.html5rocks.com/en/tutorials/es6/promises/
(#MaurizioIndenmark, #Mark Rajcok , #Michelle Tilley, #Nolan Lawson)
I couldn't use the Promise.resolve() suggested in the second reference. So I had used $q.defer() and resolve(). I guess I have to inject a dependency or something else that I missed. As shown below:
In the Controller I have:
$scope.interestedProspects = [] ;
RequestDetailsOfAsync = function ($scope) {
var deferred = $q.defer();
var id = carLists.map(function (car) {
return car.id;
}).reduce(function (previousValue, currentValue) {
return previousValue.then(function () {
TheService.AsyncRequest(currentValue).then(function (rData) {
$scope.interestedProspects.push(rData);
});
});
}, deferred.resolve());
};
In the Service I have something like:
angular.module('app', []).factory('TheService', function ($http) {
return {
AsyncRequest = function (keyID) {
var deferred = $q.defer();
var promise = authorized.get("somep.provider.api/theService.json?" + keyID).done(function (data) {
deferred.resolve(data);
}).fail(function (err) {
deferred.reject(err);
});
return deferred.promise;
}
}
}
The displayed error I got: Uncaught TypeError: previousValue.then is not a function
I made a jsfiddle reusing others available, so that it could be easier to solve this http://jsfiddle.net/alisatest/pf31g36y/3/. How to wait for AsyncRequests for each element from an array using reduce and promises
I don't know if the mistakes are:
the place where the resolve is placed in the controller function.
the way the reduce function is used
The previousValue and currentValue sometimes are seen by javascript like type of Promise initially and then as a number. In the jsfiddle I have a working example of the use of the reduce and an http request for the example.
Look at this pattern for what you want to do:
cars.reduce(function(promise, car) {
return promise.then(function(){
return TheService.AsyncRequest(car).then(function (rData) {
$scope.details.push(rData);
});
});
}, $q.when());
This will do all the asynchronous calls for every car exactly in the sequence they are in the cars array. $q.all may also be sufficient if the order the async calls are made doesn't matter.
It seems you are calling reduce on an array of ids, but assume in the passed function that you are dealing with promises.
In general, when you want to sync a set of promises, you can use $q.all
You pass an array of promises and get another promise in return that will be resolved with an array of results.

Categories