Angular Promise, why am I getting 'then' of undefined in this instance? - javascript

I have a function in my chartDirective which makes a call to a function in a service to GET data, format that data then call another function from my chartDirective:
function chartTicker(ticker, disabled) {
disabled = disabled || false;
var defer = $q.defer();
// Clear out previous chart:
d3.selectAll("svg > *").remove();
document.getElementById('chart').innerHTML = "";
chart = {},
chartData = [];
// Get and format data for chart:
document.getElementById('chart').innerHTML = "<svg></svg>";
var timeInHours = TimeSpanFactory.getTimeHours();
var promise = FormatChartDataFactory.getData(ticker, timeInHours).then(function() {
defer.resolve();
return defer.promise;
});
}
However I am getting the following error on the following line:
var promise = FormatChartDataFactory.getData(ticker, timeInHours).then(function() {
Here is my FormatChartDataFactory.getData function:
function getData(ticker, limit) {
var defer = $q.defer();
chartObj.chartData = [{}];
var limit_range = '';
if (limit > 0) {
limit_range = '?limit=' + limit;
}
getTickerPrice(ticker, limit_range).then(function() {
defer.resolve();
return defer.promise;
});
// GET Ticker data and return chartObj into drawChart:
////////////////////////////////////////////////////////////////////
function getTickerPrice(ticker, limit_range) {
return ApiFactory.getTickerQuotes(ticker.ticker, limit_range)
.success(function(data, status, headers, config) {
if (data.status === 'Success') {
// ....
Here is the link to my full FormatChartDataFactory gist file.

Promise should be return from code directly, it shouldn't be return from the .then callback of it. In short you have not returned promise from function and you are looking for .then method there, which results into an error.
Code
//1st place
var promise = FormatChartDataFactory.getData(ticker, timeInHours).then(function() {
defer.resolve();
});
return defer.promise;
//2nd place
getTickerPrice(ticker, limit_range).then(function() {
defer.resolve();
});
return defer.promise;

Related

how do I get this promise back to the controller?

So I have a series of functions I've defined in the service that upload an image to my Amazon s3 bucket, and I can console.log and alert in the service itself and everything is coming back correct.
However, now I want to return that promise to the controller so I can let the user know the upload is finished. I'm just not sure how I would do that. I've tried putting returns at the filereader.onload but then I get errors saying that what I've given back isn't a promise and such. Here's my code:
angular.module("testApp", [])
.controller("testCtrl", function($scope, amazonService) {
$scope.test = "leeroy jenkins";
$scope.upload = function() {
amazonService.uploadImage($('#file'));
}
})
.service("amazonService", function($http) {
var url = "/api/"
var uploadImageFilestream = function(obj) {
return $http({
method: "PUT",
url: url + "media/images",
data: obj
}).then(function(res) {
if (res.status === 200) {
alert("upload successful!");
}
return res;
});
}
var formatFileName = function(filename, newBase) {
//first, get the file extension
var extension = filename.substring(filename.lastIndexOf("."));
return newBase + extension;
}
this.uploadImage = function(obj) {
var file = obj[0].files[0];
var fileReader = new FileReader();
fileReader.onload = function(loaded) {
uploadImageFilestream({fileName: formatFileName(file.name, "test1"), fileBody: loaded.target.result});
}
fileReader.readAsDataURL(file);
}
})
I know that if I combined the uploadImageFilestream function with the uploadImage function it would work, but I'm not sure how to structure it with the promise in a separate function.
Use $q:
.service("amazonService", function($http, $q) {
var url = "/api/"
var uploadImageFilestream = function(obj) {
return $http({
method: "PUT",
url: url + "media/images",
data: obj
});
}
var formatFileName = function(filename, newBase) {
//first, get the file extension
var extension = filename.substring(filename.lastIndexOf("."));
return newBase + extension;
}
this.uploadImage = function(obj) {
var file = obj[0].files[0];
var fileReader = new FileReader();
var deferer = $q.defer();
fileReader.onload = function(loaded) {
uploadImageFilestream({fileName: formatFileName(file.name, "test1"), fileBody: loaded.target.result})
.then(function(res) {
if (res.status === 200) {
deferer.resolve();
alert("upload successful!");
}
return res;
});
}
fileReader.readAsDataURL(file);
return deferer.promise;
}
})
You should be using $q service of AngularJs to create deferred object and return promise.
I have modified the following code as to demonstrate way to use promises.
angular.module("testApp", [])
.controller("testCtrl", function($scope, amazonService) {
$scope.test = "leeroy jenkins";
$scope.upload = function() {
var promise = amazonService.uploadImage($('#file')); // call to function returns promise
promise.then(function(){ // when promise is resolved, desired data is passed
alert("success");
}).catch(function(error){ // when promise is rejected, related error object is passed
alert("failure");
});
}
})
.service("amazonService", function($http, $q) { // added $q service to handle promises
var url = "/api/"
var uploadImageFilestream = function(obj) {
return $http({
method: "PUT",
url: url + "media/images",
data: obj
}).then(function(res) {
if (res.status === 200) {
alert("upload successful!");
}
return res;
});
}
var formatFileName = function(filename, newBase) {
//first, get the file extension
var extension = filename.substring(filename.lastIndexOf("."));
return newBase + extension;
}
this.uploadImage = function(obj) {
var file = obj[0].files[0];
var fileReader = new FileReader();
var deferredObject = $q.defer(); // added deferred object which will be used to return promise and resolve or reject is as shown below
fileReader.onload = function(loaded) {
uploadImageFilestream({fileName: formatFileName(file.name, "test1"), fileBody: loaded.target.result}).then(response){
deferredObject.resolve(response); // when resolve function of deferred object is called success callback in controller will be called with the data you pass here
}).catch(function(errorObj){
deferredObject.reject(errorObj); // when reject function of deferred object is called error callback is controller will be called with the error object you pass here
});
}
fileReader.readAsDataURL(file);
return deferredObject.promise; // return promise object which will be resolve or reject and accordingly success callback and error callback will be called with then and catch respectively
}
});
Link to AngularJs Reference.
There are other different ways too to create and return promise you can see it in reference.
One other way to create and return promise as given in reference is using $q object as function and passing callback directly as shown below:
// for the purpose of this example let's assume that variables `$q` and `okToGreet`
// are available in the current lexical scope (they could have been injected or passed in).
// for the purpose of this example let's assume that variables `$q` and `okToGreet`
// are available in the current lexical scope (they could have been injected or passed in).
function asyncGreet(name) {
// perform some asynchronous operation, resolve or reject the promise when appropriate.
return $q(function(resolve, reject) {
setTimeout(function() {
if (okToGreet(name)) {
resolve('Hello, ' + name + '!');
} else {
reject('Greeting ' + name + ' is not allowed.');
}
}, 1000);
});
}
var promise = asyncGreet('Robin Hood');
promise.then(function(greeting) {
alert('Success: ' + greeting);
}, function(reason) {
alert('Failed: ' + reason);
});

Conditionally receiving an synchronous or asynchronous object in controller (angular, ionic)

I have the following controller:
.controller('SponsorsCtrl', function ($scope, Sponsors, $http) {
$scope.$on('$ionicView.enter', function () {
Sponsors.all($http).then(function (data) {
$scope.sponsors = data;
var check = "check";
})
});
})
The reason for using "then" is because I received an asynchronous object. Now I can however also receive a synchronous object via the following service:
(function(){
angular
.module('sponsors.services', [])
.factory('Sponsors', Sponsors);
Sponsors.$inject = [];
function Sponsors() {
var service = {
all: all,
allServer: allServer,
allLocal: allLocal,
get: get,
getTimeStamp: getTimeStamp
};
return service;
function all($http) {
var timeDifference = (Date.now() - this.getTimeStamp());
if (timeDifference < 600000) {
return this.allLocal();
}
else {
return this.allServer($http);
}
}
function allServer($http) {
return $http.get("http://dream16backend.azurewebsites.net/api/dream16/sponsors")
.then(function (resp) {
//Set localstorage, create timestamp and return the data
window.localStorage.setItem('sponsors', resp.data);
window.localStorage.setItem('sponsorsTimeStamp', Date.now());
var bla = JSON.parse(window.localStorage.getItem('sponsors'));
return bla;
}, function(err) {
console.log('ERR', err);
});
}
function allLocal() {
return JSON.parse(window.localStorage.getItem('sponsors'));
}
function get(adressId) {
for (var i = 0; i < sponsors.length; i++) {
if (sponsors[i].id === parseInt(sponsorId)) {
return sponsors[i];
}
}
return null;
}
function getTimeStamp() {
return window.localStorage.getItem('sponsorsTimeStamp');
}
}
})();
This way only the async call (function allServer) works, but the sync fails becaus: Sponsors.all(...).then is not a function
then I thought the fix was to move the "then" functionality to the all function in the service. This makes the sync call (function allLocal) work, but now the async call fails. The else condition now looks like this:
else {
this.allServer($http).then(function (data) {
return data;
})
}
And the controller looks like:
.controller('SponsorsCtrl', function ($scope, Sponsors, $http) {
$scope.$on('$ionicView.enter', function () {
$scope.sponsors = Sponsors.all($http);
var check = "check";
});
})
I verified that the call itself is working (via test variable "bla"). I also see that the controller the controller runs var check = "check"; before running the async code. What am I doing wrong here?
OK...so you need to return a promise for both instances of Sponsors.all() since one instance is already returning $http promise.
Inject $q in service so that allLocal() will also return a promise.
function allLocal() {
return $q.resolve(JSON.parse(window.localStorage.getItem('sponsors')));
}
And in controller you need to use then()
$scope.$on('$ionicView.enter', function () {
Sponsors.all($http).then(function(data){
$scope.sponsors = data;
});
var check = "check";
});
As mentioned in comments above there is no need to inject $http in controller and pass it to service when it would be simpler to just inject $http in service where it is actually needed
I'd propose you the following solution. In both cases return "Promise" object. For allLocal function it will look like this:
function allLocal() {
var deferred = $q.defer();
deferred.resolve(JSON.parse(window.localStorage.getItem('sponsors')));
return deferred.promise;
}
So now you can use .then in both cases - sync and async
I'd recommend injecting the $http service into your service.. I.e.
.factory('MyService', function ($http, $timeout,$q) {
var service = {
all: all,
allServer: allServer,
allLocal: allLocal,
get: get,
getTimeStamp: getTimeStamp
};
return service;
function all() {
var timeDifference = (Date.now() - this.getTimeStamp());
if (timeDifference < 600000) {
return this.allLocal();
}
else {
return this.allServer($http);
}
}
function allServer() {
return $http.get("http://dream16backend.azurewebsites.net/api/dream16/sponsors")
.then(function (resp) {
//Set localstorage, create timestamp and return the data
window.localStorage.setItem('sponsors', resp.data);
window.localStorage.setItem('sponsorsTimeStamp', Date.now());
var bla = JSON.parse(window.localStorage.getItem('sponsors'));
return bla;
}, function(err) {
console.log('ERR', err);
});
}
function allLocal() {
var dfd = $q.defer(); //create a deferred object
$timeout(function(){
var localResponse = JSON.parse(window.localStorage.getItem('sponsors'));;
dfd.resolve(localResponse); //resolve the localObject
});
return dfd.promise; //return the promise object so controller gets the .then function
}
function get(adressId) {
for (var i = 0; i < sponsors.length; i++) {
if (sponsors[i].id === parseInt(sponsorId)) {
return sponsors[i];
}
}
return null;
}
function getTimeStamp() {
return window.localStorage.getItem('sponsorsTimeStamp');
}
})
If you are dealing with a service that may or may not return a promise, you can use $q.when(...) to wrap that API call and let $q handle the rest.
In your case all you have to do is wrap your service api like so $q.when(Sponsors.all($http)) and use it as any regular promise.
check out https://github.com/kriskowal/q/wiki/API-Reference#promise-methods.

setTimeout issue trying to wait for execution of async

I'm new to this kind of problem in javascript and i can't fix this attempt to wait for an asynchronous call combining Angular promise objects and timeouts.
The function onTimeout seems never execute.
getAsyncContent: function (asyncContentInfos) {
var deferObj = $q.defer();
var promiseObj = deferObj.promise;
asyncContentInfos.promiseObject = promiseObj;
var blockingGuard = { done: false };
promiseObj.then(function () {
blockingGuard.done = true;
});
this.wait = function () {
var executing = false;
var onTimeout = function () {
console.log("******************** timeout reached ********************");
executing = false;
};
while (!blockingGuard.done) {
if (!executing && !blockingGuard.done) {
executing = true;
setTimeout(onTimeout, 200);
}
}
};
$http.get(asyncContentInfos.URL, { cache: true })
.then(function (response) {
asyncContentInfos.responseData = response.data;
console.log("(getAsyncContent) asyncContentInfos.responseData (segue object)");
console.log(asyncContentInfos.responseData);
deferObj.resolve('(getAsyncContent) resolve');
blockingGuard.done = true;
return /*deferObj.promise*/ /*response.data*/;
}, function (errResponse) {
var err_msg = '(getAsyncContent) ERROR - ' + errResponse;
deferObj.reject(err_msg);
console.error(err_msg);
});
return {
wait: this.wait
}
}
Client code is something like this:
var asyncVocabulary = new AsyncContentInfos(BASE_URL + 'taxonomy_vocabulary.json');
getAsyncContent(asyncVocabulary).wait();
And AsyncContentInfos is:
function AsyncContentInfos(URL) {
this.URL = URL;
this.responseData = [];
this.promiseObject;
}
$http.get returns a promise which will resolve when the call completes. Promises are a way to make asyncronous more clean and straight lined than plain old callbacks.
getAsyncContent: function (asyncContentInfos) {
return $http.get(asyncContentInfos.URL, { cache: true })
.then(function (response) {
return response.data;
}, function (errResponse) {
console.error(err_msg);
throw errResponse;
});
}
Then using it:
getAsyncContent({...}).then(function(yourDataIsHere) {
});
The nice thing about promises is that they can be easily chained.
getAsyncContent({...})
.then(function(yourDataIsHere) {
return anotherAsyncCall(yourDataIsHere);
})
.then(function(your2ndDataIsHere) {
});

Web scraping and promise with node

I am using cheerio and node to do web scraping. I thought it would be good idea to use promise for making it easier to deal with the asynchronous code. So, tried to chain the promises but could not make it working. I am pasting my code over here such that somebody could help me figure out what exactly I have been doing wrong.
http.createServer(function(req, res){
res.writeHead(200, {"Content-Type": "application/json"})
loadPage().then(parseLoadedData);
}).listen(3000, function(error){
console.log(error);
});
function fetchMainPage(){
var deferred = q.defer();
http.get('http://www.google.com?q=node', function(response){
var responseString = '';
response.on('data', function(data){
responseString += data.toString('utf8');
});
response.on('error', function(error){
deferred.reject(error);
});
response.on('end', function(){
deferred.resolve(responseString);
});
});
return deferred.promise;
}
function parseMainContent(responseString){
var deferred = q.defer();
var $ = cheerio.load(responseString);
var rightCol = $('#right-col');
var children = rightCol.children();
var keys = Object.keys(children);
var results = [];
keys.forEach(function(key){
var div = children[key];
div.children.forEach(function(aChild){
if(aChild.name == 'h3' && aChild.children[0].data == "Some title"){
lis = aChild.next.children;
var results = lis.map(function(li){
var anchor = $(li).find('a');
if(anchor != undefined && anchor.attr('href') != undefined)
return [anchor.text(), anchor.attr('href')]
});
results = results.filter(function(result){
return result != undefined;
});
deferred.resolve(results);
}
});
});
return deferred.promise;
}
var loadPage = function(){
return fetchMainPage().then(function(data){
return data;
})
},
parseLoadedData = function(data){
return parseMainContent(data).then(function(results){
console.log(results);
});
}
The problem here is I can't get my parseLoadedData being called. The response is fetched from the server but the second chaining does not seem to be working. I would like to thank you all in advance for helping me out.
Note: The url I am using is different and so the parsing function deal with that specific url only.
You don't really need the loadPage function since fetchMainPage already returns a promise so this should work:
var loadPage = function(){
return fetchMainPage();
}
To chain promises every then callback should return another promise and you were returning the data.
Eg.:
var loadPage = function(){
var deferred = q.defer();
fetchMainPage().then(function(data){
return someOtherPromise(data);
}).then(function(someOtherData) {
return myThirdPromise(someOtherData);
}).then(function(myThirdData) {
return deferred.resolve(myThirdData);
});
}
// IS THE SAME AS
var loadPage2 = function(){
return fetchMainPage().then(function(data){
return someOtherPromise(data);
}).then(function(someOtherData) {
return myThirdPromise(someOtherData);
});
}

$q not updating when the GET request returns

I am following this Angular tutorial on promises. I have a service that checks if an array is empty and if so, hits a REST service, returns a promise and updates the array. Here is the relative code:
requestingProviders: [],
getRequestingProviders: function() {
var that = this;
var deferred = $q.defer();
if(this.requestingProviders.length === 0) {
this.getResource().search({
role: 'REQUESTING_PROVIDER'
}, function(data) {
that.requestingProviders = data.providers;
deferred.resolve(data.providers);
});
return deferred.promise;
} else {
return that.requestingProviders;
}
}
The service is being called from a controller. Here is the code where it is being called:
$scope.providers = providerService.getRequestingProviders();
The REST call is made and returns fine, but the view is never updated. This is not working like the tutorial explained. Here is a plunker that shows what I am expecting. What am I doing wrong?
You need to resolve your promise:
var prom = providerService.getRequestingProviders();
prom.then(function (data) {
$scope.providers = data;
});
Also, change your code to always return the promise:
getRequestingProviders: function() {
var that = this;
var deferred = $q.defer();
if(this.requestingProviders.length === 0) {
this.getResource().search({
role: 'REQUESTING_PROVIDER'
}, function(data) {
that.requestingProviders = data.providers;
deferred.resolve(data.providers);
});
} else {
deferred.resolve(that.requestingProviders);
}
return deferred.promise;
}

Categories