AngularJS : Synchronize multiple independent promises - javascript

i'm working on an AngularJS app where Factories (that query the DB) returns promises.
The call to those Factories are encapsulated in functions such as :
function getUsers(id) {
MyUserFactory.getUsers(id).then(function(data) {
$scope.mydata = data;
...lot of code...
})
}
function updateAcordingToUserAndWeatherOfTheDay(day) {
MyWeatherFactory.getWeather(day).then(function(data) {
if ($scope.mydata.likesRain) {
...
}
})
}
then I have a :
getUser(42); updateAcordingToUserAndWeatherOfTheDay(monday);
Obviously, this works one out of two time, if the async query done by getUser() didn't had time to complete I get an error in updateAcordingToUserAndWeatherOfTheDay() on the $scope.mydata.likesRain
I know I could chain the .then() to force one query to wait for the other but
this refactoring would take a lot of time I don't have right now.
Is there another way I can wait for a promise to be completed ? At least to temporary fix the bug before a refactoring. Now the solution we have is a 3 second timer...

Refactoring this is very simple, just have the method return the promise.
function getUsers(id) {
return MyUserFactory.getUsers(id).then(function(data) {
$scope.mydata = data;
...lot of code...
})
}
function updateAcordingToUserAndWeatherOfTheDay(day) {
return MyWeatherFactory.getWeather(day).then(function(data) {
if ($scope.mydata.likesRain) {
...
}
})
}
getUsers(42).then(function() { updateAcordingToUserAndWeatherOfTheDay(day); });
This doesn't have to change any existing code (because returning the promise doesn't break any existing infrastructure), other than the code that you need to modify.
This is the fastest way to refactor to make it work.

.then() is probably the right way to go. Other solution using callbacks:
function getUsers(id, callback) {
MyUserFactory.getUsers(id).then(function(data) {
$scope.mydata = data;
...lot of code...
if (typeof callback === "function") {
callback();
}
})
}
getUser(42, function() {
updateAcordingToUserAndWeatherOfTheDay(monday);
});

Related

'await' has no effect on the type of this expression when using async/await inside nested functions

Basically Visual Studio Code gives me the note 'await' has no effect on the type of this expression. but I am note able to track the actual problem down. Of course my example here is a bit nested and therefor a bit more complex than it maybe has to be. It all starts with an array where I want to check it a value is valid. If on value fails the test, the .every function should end.
myList.every(function (value) {
if(value == null) return false;
//other stuff for preparation
return _handleValue(value);
});
value is a complex datatype so the _handleValue function is splitted into different subfunctions. Some of the function are using promises some (atm) don't. Please ignore the functions in the if block because the code is simplified and this is not causing the problem.
async function _handleValue(value) {
if (somePreChecks(value)) {
return false;
}
else if (valueIsType(value)) {
return await _someHandlerwithPromise(value); //<-- Using await here.
} else if (valueIsOtherType(value)) {
return _someOtherHandlerWithoutPromise(value);
}
}
The function _someHandlerWithPromise has some call which are using callback. Anyway this should not be a problem as long as I call reject().
function _someHandlerWithPromise(value) {
return new Promise(resolve => {
foo.acceptValue(value, function (data) {
_updateSystem(data, function (err, data) {
if(err){
//Do something
}
resolve(true);
});
});
});
}
Actually this worked pretty well elsewhere where I do not use the await function. But because the .every is not able to handle asynchronous methods, I am forced to use await.

How can I make a waterfall Q promises?

let's take this as an example:
I have 3 urls in an array urls
require function returns a promise which just makes an $http call
this is a working code, but as the array can be '1 to n' this is obviously not what I want.
I need the 3 require as a waterfall, not in parallel.
in the last promise, I need to resolve a final promise which is the var deferred.
require(urls[0]).then(function () {
require(urls[1]).then(function () {
require(urls[2]).then(function () {
deferred.resolve();
});
});
})
this approach is not working, because this will do all the $http calls in parallel.
var promises = [];
angular.forEach(urls, function (value) {
promises.push(require(value));
});
$q.all(promises).then(function () {
deferred.resolve();
});
is there a nice way to do this with a for/cycle?
Create a function to handle the iterations:
function go (urls) {
if (urls[0]) {
require(urls[0]).then(function () {
go(urls.slice(1));
});
}
}
go(urls);
Here is an excellent blog post: http://www.codeducky.org/q-serial/
I will share only the part that is relevant.
First we define this helper method:
function serial(tasks) {
var prevPromise;
angular.forEach(tasks, function (task) {
//First task
if (!prevPromise) {
prevPromise = task();
} else {
prevPromise = prevPromise.then(task);
}
});
return prevPromise;
}
Then we use it.
serial([
function() { return require(urls[0]) },
function() { return require(urls[1]) },
function() { return require(urls[2]) }
]).then(function () {
deferred.resolve();
});
Just to offer another way, there's a "one-line" solution to this without having to make another method:
return promises.reduce($q.when, promises[0]);
See demo here: http://plnkr.co/edit/xlGxYj57lzXdAMM5Iv6s?p=preview (I changed the default $q.when to something else to show handling each promise.)
Update: Made the plunker more representative of OP's scenario.

call async function for each array element of a promise result object, make changes and return this object

A Bluebird Promise returns an object that contains two arrays of objects, cars and contracts. Then, I want to iterate over the cars, call an asynchronous function and, based on the returned value, make some changes to the second array and return the initial result object with these changes. I can't figure out how to do this with promises. Or with async, for that matter. I feel like they should be nested promises, but i can;t get it to work at all.
the version with promises:
somePromise().then(function (result) {
Promise.each(result.cars, function (car) {
makeAsyncCall(car.id, function (err, resultArray) {
if (err) {
throw new Error();
}
result.contracts.forEach(function (contract) {
if (resultArray.indexOf(contract.id) > -1) {
contract.car = car.id;
}
});
});
}).then(function (eachResult) {
//eachResult is result.firstArray, which is not interesting.
return result;
});
}).then(function (result)) {
//this gets called before my promise.each gets executed??
}
Can anyone give me a hint as to where my mistake is?
Have a look at my rules of thumb for promise development. The two specific points that apply to your code are:
promisify your async callback-taking functions before using them, specifically
var makeCall = Promise.promisify(makeAsyncCall);
always return promises from your functions that do asynchronous things. This is especially true for callbacks, like the function() { Promise.each(…).then(…) } and the function() { makeAsyncCall(…) }.
With those, you should get to the following:
somePromise().then(function(result) {
return Promise.each(result.cars, function(car) {
return makeCall(car.id).then(function(resultArray) {
// a lookup structure of contracts by id could make this more efficient
result.contracts.forEach(function (contract) {
if (resultArray.indexOf(contract.id) > -1)
contract.car = car.id;
});
});
}).return(result);
}).…

Asynchronous Angular Promise and Variable Initialization

I have a factory that looks like such:
app.factory('thingFactory', function($http) {
var factory = {};
var things = [];
factory.refreshThings = function() {
return $http.post('/GetThings');
}
factory.initializeThings = factory.refreshThings()
.then(function(response) {
things = response.data;
}, function(response){
// some error handling code here...
});
factory.getThings = function() {
return things;
}
return factory;
}
and a controller
app.controller('myController', function($scope, thingFactory) {
$scope.things = thingFactory.getThings();
}
Because of the asynchronous nature of promises, and other collections being initialized (in addition to things), should I be concerned with getThings() returning an empty array, and thus, returning before the $http.post() call has resolved?
Is this a better alternative?
app.controller('myController', function($scope, thingFactory) {
$scope.things = []
thingFactory.initializeThings
.then(function(response) {
$scope.things = response.data;
}, function (response) {
// some error handling code here...
});
}
Is there a safe alternative, where I can get the controller to not think about the promise and just safely get the collection from the factory?
You're code is definitely going to be problematic. The factory will not be instantiated until it is used by a controller, so things will not be initialized until it is called by a controller, at which time initializeThings will get called right before you call getThings, which will likely return an empty array. Also, it's never a good idea to follow a "let's hope it's there" approach.
I see two approaches you can take: getThings accepts a callback as an argument or it returns a promise, which could look something like this:
Callbacks - I prefer callbacks over promises, but that's a personal thing. Also, I use NodeJS-inspired syntax:
var things; // no need to initialize array
// callback -> function (error, things) {}
factory.getThings = function (callback) {
if (things) {
return callback(null, things);
}
factory.refreshThings()
.then( function (response) {
things = response.data;
return callback(null, things);
}, function (reason) {
return callback(reason);
});
}
Promises - not the best syntax, but you get the idea
factory.getThings = function () {
var d = $q.defer();
// I'm sure there's a better way to do this, but setting
// this in a timeout will allow the promise to return so that
// you can try to return it.
$timeout( function () {
if (things) {
return d.resolve(things);
}
factory.refreshThings()
.then( function (response) {
things = response.data;
return d.resolve(things);
}, function (reason) {
return d.reject(reason);
});
});
return d.promise;
}
As a side note, if you're using a RESTful API, GET should be used to get information rather than POST.

Is it possible to make a function not return until a condition is met?

My problem is pretty simple and I think lot of programmers already must have faced this.
I am using angularjs and my javascript looks as follow:
controller('myController', function())
{
if(validateForm())
{
//do after-validation stuff
}
validateForm = function()
{
var val = undefined;
$http.get('api/validate/').
success(function(data){
val = true;
}).
error(function(data){
val =false;
});
while(!val)
{ // loop over till val has value
}
return val;
}
}
I have two questions:
1) when I make http call it returns a promise object and it resolves to either success or error function. If that is the case then the while loop in this code should not be infinite, but it is. By debugging I found that if I assign a value to variable 'var' then only the http call is made i.e when the loops ends.
why http call, being a deferred call, waits on the while loop to finish?
2) If I remove the while loop, no matter what the value of 'var' is, the function returns. How can I make it return only if 'var' is defined?
JavaScript is single threaded, so the http success/failure never get executed because the while loop is going. This is causing your infinite loop.
Return the promise and use that in the calling function.
Well usually ajax calls like these are not synchronous, having said that, when your while loop is hit your variable val is not yet set. What you should do is, just call a function in either success or error case and that function should have your while loop. Or try to make async ajax call which I am not sure if possible with selected method. So your new code will be something like
function mycallback(someval){
while(!someval)
{ // loop over till val has value
}
}
controller('myController', function())
{
if(validateForm())
{
//do after-validation stuff
}
function()
{
var val = undefined;
$http.get('api/validate/').
success(function(data){
val = true;
mycallback(val);
}).
error(function(data){
val =false;
mycallback(val);
});
}
}
depending on your need you need to change flow to meet this sync/async call. I don't recommand syncing the call as it slow down the execution for time your get ajax response. You should practice to use Async call only.
Refactor, and use the .then
angular.module("myModule")
.controller("myController", ["$scope", "$http", function ($scope, $http) {
var controller = this;
angular.extend(controller, {
// ...
submit : submit,
handleSuccess : function () { /* ... success */ },
handleError : function (err) { /* error */ }
});
function submit (form) {
validateForm(form)
.then(controller.handleSuccess, controller.handleError)
.then(controller.cleanUp);
}
function validateForm (form) {
return $http.get("...").then(function (response) { return response.data; });
}
}]);
It should all be readable, now.
Additionally, the logic doesn't go inside of "success", instead it goes inside of subsequent .then calls, so you can keep your submit clean and readable.
But you can not ever do something like
while (true) {
}
or
for (var i = 0; i < 10000000; i += 1) { }
All you are doing is locking your browser, preventing people from using the page.
You can't animate CSS, you can't read AJAX values, you probably can't even close the tab (depending on the browser).
Everything should either be reactive (event-based) or asynchronous, using callbacks or promises.

Categories