I have a problem with the following code:
initPromise = $q.all(arrayOfPromises)
.then(function () {
return $scope.methodWhichReturnsPromise()
.then(function (data) {
console.log("report data");
return data;
});
});
if ($scope.showCompare) {
initPromise
.then(function () {
return $q.all(anotherArrayOfPromises);
})
.then(function () {
return aMethodWhichReturnsAPromise().then(function () {
console.log("compare report data");
});
});
}
initPromise
.then(function () {
console.log("generate view data");
})
.finally(function () {
console.log("finally");
});
I'm loading a bunch of async data when loading a controller based on route parameters. And if the flag showCompare is there, I want to load something in between. But the order of the console.log messages is the following:
report data
generate view data
finally
compare report data
I was expecting that compare report data would show up exactly in the same order it was written in the code.
What am I doing wrong?
You are adding two distinct handler on the initPromise, instead of chaining all .then() calls. To do so, you would need to use
if ($scope.showCompare) {
initPromise = initPromise.then(…);
}
Related
I want to refresh my indexeddb store with new data after a successful login. After the data refresh is complete, I want to redirect to the landing page. My problem is that I have 1000+ calls to setItem and they aren't finishing.
var app = {
Login: function () {
WebService.Login($("#username").val(), $("#password").val())
.then(function () {
// TODO: refresh data and then redirect...
UpdateData().then(function() {
window.location.href = '/Home';
});
})
.catch(function (err) {
console.log("error logging in");
});
},
UpdateData: function () {
return fetch('/api/Customer').then(function (response) {
return response.json();
})
.then(function (data) {
var customerStore = localforage.createInstance({ name: "customers" });
// Refresh data
customerStore.clear().then(function () {
data.forEach(function (c) {
// How do I know when all setItem calls are complete??
customerStore.setItem(String(c.CustomerID), c);
});
});
})
.catch(function (err) {
console.log("Data error", err);
});
}
}
I'm still relatively new to promises but there must be a way I can get all of the setItem calls into a Promise.all() that I can return. How can I do this?
I think that you need something like this:
return fetch("/api/Customer")
.then(function(response) {
return response.json();
})
.then(function(data) {
var customerStore = localforage.createInstance({ name: "customers" });
// Refresh data
return customerStore.clear().then(function() {
return Promise.all(
data.map(function(c) {
return customerStore.setItem(String(c.CustomerID), c);
})
);
});
})
.catch(function(err) {
console.log("Data error", err);
});
data.map will return an array of promises and then we also return the aggregate promise (from Promise.all).
You should also keep a reference of the customerStore for later use.
Also, if the amount of data is huge, you might want to use localForage-setItems to make the operation a bit more performant (but try to avoid a possible premature optimization).
What I am trying to get done is extend JSON object in service and then pass it to controller.
JSON came to service from another service which makes backend call.
The code is pretty complicated so I add comments and console.logs:
//get games config object from another service
gamesConfig: gamesConfigService.gamesConfig(),
// prepare name of games icons. This is support function executed in next method
transformSpace: function(subject) {
var ensuredSubject = subject.toString().toLowerCase();
var transformedSubject = ensuredSubject.replace(/ /g, '_');
return transformedSubject;
},
//add iconname property to game config object
extendGameConfig: function() {
var that = this;
this.gamesConfig
.then(function (response) {
console.log(response.data); // this works and console.log my JSON
response.data.map(function(obj) {
return new Promise(function(res){
angular.extend(obj, {
iconname: that.transformSpace(obj.attributes.name) + "_icon.png"
});
});
});
}, function () {
console.log('errror');
});
This contains one support method transformSpace and main method which is not passing data correctly. ( I think )
I'm trying to receive this promise in controller by:
theService.getGamesObj.extendGameConfig()
.then(function (response) {
$scope.allGames = response;
console.log($scope.allGames);
}, function () {
console.log('err')
});
And then I'll use it in view. For now code above doesn't work and give me following error:
TypeError: Cannot read property 'then' of undefined
I've added comments where I think your code has gone wrong
extendGameConfig: function() {
// ***********
// use => functions, that = this wont be needed
var that = this;
// ***********
// if you want this this function to return something, add a return
// this is why you get the
// Cannot read property 'then' of undefined error
// as this function returns undefined
this.gamesConfig
.then(function (response) {
console.log(response.data); // this works and console.log my JSON
// ***********
// you're using .map ... and discarding the result!
response.data.map(function(obj) {
// ***********
// you're creating a promise that never resolves!
// also, why are you promisifying synchronous code?
return new Promise(function(res){
angular.extend(obj, {
iconname: that.transformSpace(obj.attributes.name) + "_icon.png"
});
});
});
}, function () {
console.log('errror');
});
so, try this
extendGameConfig: function() {
return this.gamesConfig
.then(response => {
return response.data.map(obj => {
return angular.extend(obj, {iconname: this.transformSpace(obj.attributes.name) + "_icon.png"});
});
}, function () {
console.log('errror');
});
or, better yet
extendGameConfig: function() {
return this.gamesConfig
.then(response =>
response.data.map(obj =>
angular.extend(obj, {iconname: this.transformSpace(obj.attributes.name) + "_icon.png"})
)
)
.catch(function (err) {
console.log('error', err);
throw err; // log the error, but you'll probably want to reject this promise so the calling code doesn't think there is success?
});
}
I am trying to delete a post from a list. The delete function is performing by passing serially to a delete function showed below.
$scope.go = function(ref) {
$http.get("api/phone_recev.php?id="+ref)
.success(function (data) { });
}
After performing the function, I need to reload the http.get request which used for listing the list.
$http.get("api/phone_accept.php")
.then(function (response) { });
Once the function performed. The entire list will reload with new updated list. Is there any way to do this thing.
Try this
$scope.go = function(ref) {
$http.get("api/phone_recev.php?id="+ref)
.success(function (data) {
//on success of first function it will call
$http.get("api/phone_accept.php")
.then(function (response) {
});
});
}
function list_data() {
$http.get("api/phone_accept.php")
.then(function (response) {
console.log('listing');
});
}
$scope.go = function(ref) {
$http.get("api/phone_recev.php?id="+ref)
.success(function (data) {
// call function to do listing
list_data();
});
}
Like what #sudheesh Singanamalla says by calling the same http.get request again inside function resolved my problem.
$scope.go = function(ref) {
$http.get("api/phone_recev.php?id="+ref).success(function (data) {
//same function goes here will solve the problem.
});}
});
You can use $q - A service that helps you run functions asynchronously, and use their return values (or exceptions) when they are done processing.
https://docs.angularjs.org/api/ng/service/$q
Inside some service.
app.factory('SomeService', function ($http, $q) {
return {
getData : function() {
// the $http API is based on the deferred/promise APIs exposed by the $q service
// so it returns a promise for us by default
return $http.get("api/phone_recev.php?id="+ref)
.then(function(response) {
if (typeof response.data === 'object') {
return response.data;
} else {
// invalid response
return $q.reject(response.data);
}
}, function(response) {
// something went wrong
return $q.reject(response.data);
});
}
};
});
function somewhere in controller
var makePromiseWithData = function() {
// This service's function returns a promise, but we'll deal with that shortly
SomeService.getData()
// then() called when gets back
.then(function(data) {
// promise fulfilled
// something
}, function(error) {
// promise rejected, could log the error with: console.log('error', error);
//some code
});
};
I am trying to write a promise function using Bluebird library for nodejs. I want to return 2 variables from my function.
I want the first function to return immediately and the second to complete its own promise chain before returning.
function mainfunction() {
return callHelperfunction()
.then(function (data) {
//do something with data
//send 200 Ok to user
})
.then(function (data2) {
//wait for response from startthisfunction here
})
.catch(function (err) {
//handle errors
});
}
function callHelperfunction() {
return anotherHelperFunction()
.then(function (data) {
return data;
return startthisfunction(data)
.then(function () {
//do something more!
})
});
}
Just like regular functions only have one return value, similarly promises only resolve with one value since it's the same analogy.
Just like with regular functions, you can return a composite value from a promise, you can also consume it using .spread for ease if you return an array:
Promise.resolve().then(function(el){
return [Promise.resolve(1), Promise.delay(1000).return(2));
}).spread(function(val1, val2){
// two values can be accessed here
console.log(val1, val2); // 1, 2
});
The only thing that appears to be wrong is the expectation that do something with data; send 200 Ok to user; should be performed in mainfunction(), part way through the promise chain in callHelperfunction().
This can be overcome in a number of ways. Here's a couple :
1. Move do something with data; send 200 Ok to user; into callHelperfunction()
function mainfunction() {
return callHelperfunction())
.catch(function (err) {
//handle errors
});
}
function callHelperfunction() {
return anotherHelperFunction()
.then(function (data1) {
//do something with data
//send 200 Ok to user
return startthisfunction(data1)
.then(function (data2) {
//wait for response from startthisfunction here
//do something more!
});
});
}
2. Dispense with callHelperfunction() altogether and do everything in mainfunction()
function mainfunction() {
return anotherHelperFunction()
.then(function (data1) {
//do something with data1
//send 200 Ok to user
return startthisfunction(data1);
})
.then(function (data2) {
//wait for response from startthisfunction here
})
.catch(function (err) {
//handle errors
});
}
as I understand it Angular http has 2 checks 'success and 'error'. Thats in terms of connecting to the service or not - so I have that in hand and thats my first check.
The issue I have is that the data in my JSON has a success state which informs me if the data it contains or has received from my form had any problems with it, in which case there will be an error object that I act on and display to the user.
I need to check for that value of success, but where is the best place to check for that?
Should I be doing it in the controller?
Without that data being correct theres nothing else for the page to do so it is effectively the first thing that needs to be done after the data is retrieved.
heres the basic controller layout
app.controller("dataCtrl", function ($scope, $http) {
$http.post('/getdata').success(function (data) {
$scope.businessData = data;
// Should I then be checking businessData.success at this level?
}).error(function () {
alert("Problem");
});
});
You can write something like this:
$http.post('/getdata').success(function (data) {
if (validate(data)) {
$scope.businessData = data;
} else {
$scop.buisnessDataError = {msg: 'smth bad happend'};
}
}).error(function () {..})
Otherwise, you can write your validator in Promise-like style and then just chain promises in such manner:
$http.post('/getdata').then(function (res) {
return validator(null, res.data);
}, function (err) {
return validator({msg: 'error'})
}).then(function (data) {
//proceed your data
}, function (err) {
alert(err.msg);
});
Where validator is:
var varlidator = function (err, data) {
return $q(function (resolve, reject) {
if (/*data is not valid*/ || err) {
reject(err);
} else {
resolve(data);
}
});
}
$q is a standard angulars implementation of Promises