How to implement promise into function so i can get rid off timeout, if it is even possible? I'm getting some data from factory 'Prim',function look's like :
$scope.getPre = function(id){
var url = WEB_API.MainUrl + '/api/prim/' + id +'/' + $window.sessionStorage.getItem('idsom');
Prim.getprim(function(data) {
$scope.prim1 = data;
$scope.prim = $scope.prim1[0];
}, url);
$scope.$apply();
}
and the timeout i want to rid off :
setTimeout(function() { // it can't work without timeout
$scope.getPre($routeParams.idprim);
}, 100);
Your function should return a promise, so you could use .then() on it:
$scope.getPre = function(id){
var deferred = $q.defer()
var url = WEB_API.MainUrl + '/api/prim/' + id +'/' + $window.sessionStorage.getItem('idsom');
Prim.getprim(function(data)
$scope.prim1 = data;
$scope.prim = $scope.prim1[0];
deferred.resolve();
}, url);
return deferred.promise;
}
And then just use it like this:
$scope.getPre($routeParams.idprim).then(function () {
// Do whatever you did after setTimeout
});
Related
I´m trying to understand how promises work in Angular 1.0.7, but the syntax and the concept are difficult to me. I created a related previous post Nesting promises with $resources in AngularJS 1.0.7 that is working fine. However, when I try to do the same but replacing the $resource with an $http service, then it´s not working for me as expected.
The code never wait for the promise to come and I don´t get any result.
I attach the code:
// URL has to be parsed to get the destination, language and boatType
var parseURL = function() {
var language = $routeParams.language;
var url = $location.path();
var deferred = $q.defer();
var promise = deferred.promise;
promise.then(function success(result) {
var destination = result;
console.log("destination:" + destination);
searchBoats(destination);
});
parseDestination(url, language).then(deferred.resolve);
};
parseURL();
var parseDestination = function(url, language) {
console.log("parseDestination.begin");
var departure = UrlService.parseUrlDeparture(url);
var deferred = $q.defer(),
promise = deferred.promise;
TranslationService.getTranslatedDeparture(departure, language, API_SERVER_URL, deferred.resolve, deferred.reject);
return promise;
};
// The function in the service
getTranslatedDeparture: function(destination, language, api) {
var defered = $q.defer();
var destinationPromise = defered.promise;
$http.get("http://" + api + "/translatedDepartures?departure=" + destination + ";lang=" + language + ";matchStart=" + true).then(
//var destination = result.data.map(function (source) { return source.element_translation; });
defered.resolve
);
return destinationPromise;
}
You are using promises wrong in just about every way imaginable. Promises are intended to be chained and create new promises using .then(). And doing this will fix your bugs and cut the length of your code in half. As long as you have a promise to start with (which you do because $http.get returns a promise), you don't need, and shouldn't use, $q.defer():
// URL has to be parsed to get the destination, language and boatType
var parseURL = function() {
var language = $routeParams.language;
var url = $location.path();
parseDestination(url, language)
.then(function (result) {
var destination = result;
console.log("destination:", destination);
searchBoats(destination);
});
};
parseURL();
var parseDestination = function(url, language) {
console.log("parseDestination.begin");
var departure = UrlService.parseUrlDeparture(url);
return TranslationService.getTranslatedDeparture(departure, language, API_SERVER_URL);
};
// The function in the service
getTranslatedDeparture: function(destination, language, api) {
var url = "http://" + api + "/translatedDepartures?departure=" + destination + ";lang=" + language + ";matchStart=" + true;
return $http.get(url)
.then(function (result) { return result.data; });
}
With a background in object oriented languages I'm trying to learn some jquery, and wrap my head around asynchronous calls. My intention was to create a javascript object to contain results of async api calls, and be able to ask said object if it was done loading.
I have been trying to do it using Deferred's from jquery, and I have no problem to get snippets to work as in the documentations examples, but it won't execute in the order I expect when I put the Deferred inside my class.
How do I create javascript objects with $.Deferred as member variables?
(the timeouts in my attached code are to mimic delays in api calls)
<!DOCTYPE html>
<html>
<body>
<script src="jquery-3.1.1.js"></script>
<script>
//top level
var isdone = false;
var def = $.Deferred();
$.when(def).done(function() {
var msg = "checking topLevel isdone " + isdone;
console.log(msg);
$("body").append("<p>" + msg + "</p>");
})
var delayedCall = function() {
setTimeout(function() {
//resolve after 5 seconds
isdone = true;
def.resolve();
}, 1000);
}
delayedCall();
//inside function
function DelayedObject()
{
this.defThis = $.Deferred();
var defVar = $.Deferred();
this.delayedObjCall = function()
{
setTimeout(function()
{
//resolve after 5 seconds
isdone = true;
this.def.resolve();
}, 1000);
}
this.delayedObjCall();
this.getStatusThis = function()
{
return this.def;
}
this.getStatusVar = function()
{
return this.def;
}
}
delObj = new DelayedObject();
$.when(delObj.getStatusThis() ).done(function() {
var msg = "checking delObj (this) isdone " + isdone;
console.log(msg)
$("body").append("<p>" + msg + "</p>");
});
$.when(delObj.getStatusVar() ).done(function() {
var msg = "checking delObj (var) isdone " + isdone;
$("body").append("<p>" + msg + "</p>");
console.log(msg)
});
$(window).on("load", function()
{
var msg = "<p>" + " Page loaded " + "</p>";
$("body").append(msg);
console.log(msg);
});
</script>
</body>
</html>
Result
checking delObj (this) isdone false
checking delObj (var) isdone false
Page loaded
checking topLevel isdone true
Some issues:
You refer to the wrong objects/variables in some places (this.def does not exist, this.defThis and defVar are never used)
this is not defined in a timeout callback function (or is window in sloppy mode), so you need to use a solution for that (several possibilities, e.g. bind)
You never resolve defVar
You use one isdone variable, so do realise that once it is set to true it remains true and says little about the other promises.
Here is corrected code (simplified: omitting the change of the document content):
var isdone = false;
var def = $.Deferred();
$.when(def).done(function() {
console.log("checking topLevel isdone " + isdone);
isdone = false; // set back to false
});
var delayedCall = function() {
setTimeout(function() {
isdone = true;
def.resolve();
}, 500); // Half a second
}
delayedCall();
//inside function
function DelayedObject() {
this.defThis = $.Deferred();
var defVar = $.Deferred();
this.delayedObjCall = function() {
setTimeout(function() {
//resolve after 5 seconds
isdone = true;
this.defThis.resolve(); // refer to the correct object
}.bind(this), 1000); // provide the correct context with bind
// Also resolve the other deferred:
setTimeout(function() {
//resolve after 5 seconds
isdone = true;
defVar.resolve();
}.bind(this), 1500); //... a bit later
}
this.delayedObjCall();
this.getStatusThis = function() {
return this.defThis; // return correct object
}
this.getStatusVar = function() {
return defVar; // return correct object
}
}
delObj = new DelayedObject();
$.when(delObj.getStatusThis() ).done(function() {
console.log("checking delObj (this) isdone " + isdone);
isdone = false; // set back to false
});
$.when(delObj.getStatusVar() ).done(function() {
console.log('checking delObj (var) isdone ' + isdone)
isdone = false; // set back to false
});
$(window).on("load", function() {
console.log('Page loaded');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
When I call this function it returns a result immediately instead of delaying to after the third promise. What can I do??
getBlogs: function(blogId){
var blogcomments = new Entities.BlogCommentCollection();
var blogs = new Entities.BlogCollection();
var defera = $.Deferred();
var deferb = $.Deferred();
var deferc = $.Deferred();
var model;
//alert(model);
$.get("/lightning/presentation/blogs", function(val){
defera.resolve(val);
});
var promisea = defera.promise();
$.when(promisea).done(function(val){
var models = initializeBlogs(JSON.parse(val));
blogs.reset(models);
model = blogs.at(blogId);
//alert(JSON.stringify(model));
$.get("/lightning/presentation/blogs/?blogId=" + blogId, function(full){
deferb.resolve(full);
});
});
var promiseb = deferb.promise();
$.when(promiseb).done(function(full){
model.set('full', full);
//alert(JSON.stringify(model));
$.get("/lightning/presentation/blogs/?comments=" + blogId, function(res){
deferc.resolve(res);
});
});
var promisec = deferc.promise();
$.when(promisec).done(function(res){
if(res.length === 0){
blogcomments.reset();
}else{
//alert(res)
var models = initializeBlogComments(JSON.parse(res));
blogcomments.reset(models);
model.set('comments', blogcomments)
//return model;
}
currentBlog = model;
alert(JSON.stringify(model));
//return model;
});
//alert(JSON.stringify(model));
return model;
},
Promises are async. They don't cause the current function to delay return (that would be sync). They return, well, a promise that will resolve at some point in the future.
So the most basic way to fix your code is to return not the model, but the promise
getBlogs: function(blogId) {
var deferAll = $.Deferred();
// your last set will then respond to them all
$.when(promisec).done(function(res){
if(res.length === 0){
blogcomments.reset();
}else{
//alert(res)
var models = initializeBlogComments(JSON.parse(res));
blogcomments.reset(models);
model.set('comments', blogcomments)
//return model;
}
currentBlog = model;
// THIS is where it gets resolved
deferAll.resolve(model);
});
// do whatever you need to
return deferAll.promise();
}
And then you call getBlogs as
getBlogs(25).then(function(model) {
// model is given here
});
However, there are better ways to do this. First of all, you can chain promises.
$.get("/lightning/presentation/blogs/?blogId=" + blogId).then(function(full){...}).then().then() // etc
Lastly, if you really are going to do multiple async things in an order like that, may I recommend caolan's excellent async library? It makes thinks like this much easier. https://github.com/caolan/async
getBlog: function(blogId, model){
var blogcomments = new Entities.BlogCommentCollection();
var defer = $.Deferred();
var fullBlog;
$.when(
$.get("/lightning/presentation/blogs/?blogId=" + blogId, function(full){
fullBlog = full;
}),
$.get("/lightning/presentation/blogs/?comments=" + blogId, function(res){
var models = initializeBlogComments(JSON.parse(res));
blogcomments.reset(models);
})
).done(function() {
model.set('full', fullBlog);
model.set('comments', blogcomments);
defer.resolve(model);
});
return defer.promise();
},
Improving upon the answer you already provided, you can do it this way that avoids creating the extra deferred and just uses the promise that $.when() already returns. :
getBlog: function(blogId, model){
var blogcomments = new Entities.BlogCommentCollection();
var fullBlog;
return $.when(
$.get("/lightning/presentation/blogs/?blogId=" + blogId, function(full){
fullBlog = full;
}),
$.get("/lightning/presentation/blogs/?comments=" + blogId, function(res){
var models = initializeBlogComments(JSON.parse(res));
blogcomments.reset(models);
})
).then(function() {
model.set('full', fullBlog);
model.set('comments', blogcomments);
return model;
});
},
Or, you could additionally use the return values from $.when() to avoid the separate ajax callbacks like this:
getBlog: function(blogId, model){
var blogcomments = new Entities.BlogCommentCollection();
return $.when(
$.get("/lightning/presentation/blogs/?blogId=" + blogId),
$.get("/lightning/presentation/blogs/?comments=" + blogId)
).then(function(r1, r2) {
model.set('full', r1[0]);
var models = initializeBlogComments(JSON.parse(r2[0]));
blogcomments.reset(models);
model.set('comments', blogcomments);
return model;
});
},
I have a provider for my REST-Services named MyRestServices:
app.provider('MyRestServices', function() {
this.baseUrl = null;
this.setBaseUrl = function(_baseUrl) {
this.baseUrl = _baseUrl;
};
this.$get = ['$http', function($http) {
var _baseUrl = this.baseUrl;
function getMyData() {
return $http.get(_baseUrl + 'data1/?token=' + token + '&key=' + key);
}
function preGetTokenAndKey() {
return $http.get(_baseUrl + 'keyAndToken/');
}
return {
getMyData: getMyData,
preGetTokenAndKey: preGetTokenAndKey
};
}];
});
I configure it before calling the first REST service.
app.config(function(MyRestServicesProvider) {
MyRestServicesProvider.setBaseUrl('https://www.test.com/rest/');
});
And then I have a HeadCtrl controller which should call preGetTokenAndKey to get key and token which is needed for some other REST calls like getMyData.
app.controller('HeadCtrl', function (MyRestServices) {
MyRestServices.preGetTokenAndKey().success(function(data) {
var key = data.dataSection.key;
var token = data.dataSection.token;
});
});
My problem is I want to call getMyData from another controller, but I need key and token to make this call.
So I need to wait until preGetTokenAndKey was successful and I have to provide the two values to the MyRestServices provider.
How can I solve these problems?
It sounds like a better solution would be to chain them within your service itself. You'd setup your own promise within preGetTokenAndKey, which gets resolved by the $http call. Subsequent calls to preGetTokenAndKey() would just return the already resolved data w/o making additional $http calls.
So something along the lines of the following should get you started:
app.provider('MyRestServices', function() {
this.baseUrl = null;
this.setBaseUrl = function(_baseUrl) {
this.baseUrl = _baseUrl;
};
this.$get = ['$http', function($http) {
var _baseUrl = this.baseUrl;
var _tokenAndKey = {};
function getMyData() {
return preGetTokenAndKey().then(function (data) {
return $http.get(_baseUrl + 'data1/?token=' + data.dataSection.token + '&key=' + data.dataSection.key);
});
}
function preGetTokenAndKey() {
if(!_tokenAndKey.set) {
_tokenAndKey.deferred = $http.get(_baseUrl + 'keyAndToken/').then(function(data) {
_tokenAndKey.set = true;
return data;
});
}
return _tokenAndKey.deferred.promise;
}
return {
getMyData: getMyData,
preGetTokenAndKey: preGetTokenAndKey
};
}];
});
My problem is I want to call getMyData from another controller,
If so, you can use $broadcast to notify other controller that async call resolved and you have key/token
app.controller('HeadCtrl', function($rootScope, MyRestServices) {
MyRestServices.preGetTokenAndKey().success(function(data) {
var key = data.dataSection.key;
var token = data.dataSection.token;
$rootScope.$broadcast("getMyDataTrigger", {key: key,token: token});
});
});
In other controller implement listener:
$rootScope.$on("getMyDataTrigger", function(event, data){
if(data){
MyRestServices.getMyData(data.key, data.token);
// ...
}
});
Just override getMyData:
function getMyData(key, token) {
return $http.get(_baseUrl + 'data1/?token=' + token + '&key=' + key);
}
I'm writing a library to access data from a server, and return formatted data to the consumer of my functions.
Here is an example of what I'd like to have:
// my code
var model = function () {
return $.ajax(myRequest).done(function (rawData) {
return treatment(data);
});
}
// client code
var useModel = function () {
var modelPromise = model();
modelPromise.done(function (formattedData) { // consume this result })
}
where formattedData is the result of my first done callback and not the rawData.
Do you have any ideas?
Thanks
R.
Regis,
jQuery's documention for .then() says :
As of jQuery 1.8, the deferred.then() method returns a new promise
that can filter the status and values of a deferred through a
function, replacing the now-deprecated deferred.pipe() method.
The second example for .then() is similar to what you want (though not involving ajax).
As far as I can tell, the necessary changes to your code are very minimal :
// my code
var model = function () {
return $.ajax(myRequest).then(function (rawData) {
return treatment(rawData);
});
}
// client code
var useModel = function () {
var modelPromise = model();
modelPromise.done(function (formattedData) { // consume this result })
}
Regis,
I like Beetroot's answer a lot. Here's an example I made while trying to understand this concept for myself: Multiple asynchronous requests with jQuery .
Source from jsFiddle:
var logIt = function (msg) {
console.log(((new Date()).toLocaleTimeString()) + ": " + msg);
}, pauseBrowser = function (ms) {
ms += new Date().getTime();
while (new Date() < ms) {}
}, dataForService1 = {
json: JSON.stringify({
serviceNumber: 1,
description: "Service #1's data",
pauseAfterward: 3 // for pausing the client-side
}),
delay: 0 // delay on the server-side
}, dataForService2 = {
json: JSON.stringify({
serviceNumber: 2,
description: "Service #2's data",
pauseAfterward: 1
}),
delay: 0 // delay on the server-side
};
function getAjaxConfiguration() {
return {
type: 'POST',
url: '/echo/json/',
success: function (data) {
var msg = "Handling service #" + data.serviceNumber + "'s success";
logIt(msg);
logIt(JSON.stringify(data));
}
};
}
var async2 = function () {
var ajaxConfig = $.extend(getAjaxConfiguration(), {
data: dataForService2
});
return $.ajax(ajaxConfig);
};
var async1 = function () {
var ajaxConfig = $.extend(getAjaxConfiguration(), {
data: dataForService1
});
return $.ajax(ajaxConfig);
};
var do2AsynchronousFunctions = function () {
var dfd = new $.Deferred();
async1()
.then(function (async1ResponseData) {
logIt("async1's then() method called, waiting " + async1ResponseData.pauseAfterward + " seconds");
pauseBrowser(async1ResponseData.pauseAfterward * 1000);
})
.done(function (a1d) {
logIt("async1's done() method was called");
return async2()
.then(function (async2ResponseData) {
logIt("async2's then() method called, waiting " + async2ResponseData.pauseAfterward + " seconds");
pauseBrowser(async2ResponseData.pauseAfterward * 1000);
})
.done(function (a2d) {
logIt("async2's done() method was called");
dfd.resolve("final return value");
});
});
return dfd.promise();
};
$.when(do2AsynchronousFunctions()).done(function (retVal) {
logIt('Everything is now done! Final return value: ' + JSON.stringify(retVal));
});