I have some issue with the return of promise. Before, during an http call, I used a function like this returning one promise:
get_data: function (url)
{
let timestamp = new Date();
return $http({
method: "GET",
url: url
headers: {
'timestamp': timestamp,
}
}).then(
function successCallback(response)
{
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
},
function errorCallback(response)
{
console.dir(response);
return response;
});
},
It was quite straight forward and I could use it like this:
get_data('my_awesome_url').then(function(response){
let my_awesome_data = response
})
The culprit is the timestamp thingy. I use it for some authentification, the why is not important, but by getting it from the client side I was quite often victim of bad horloge or system local set in another langage.
My solution was to create a function that request a server timestamp . But by doing this I must first wait for the timestamp request to hand, then launch another request and... wait for it to end.
This is where I don't really know what to do. My code look like this:
get_data: function (url)
{
let timestamp = new Date();
get_timestamp().then(function(){
return $http({
method: "GET",
url: url
headers: {
'timestamp': timestamp,
}
}).then(
function successCallback(response)
{
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
},
function errorCallback(response)
{
console.dir(response);
return response;
});
});
},
But I'm not sure what I should return. Should I return the get_timestamp promise and in the "then" wait for the other request to end? Should I make the get_timestamp a synchronous call because after all it's just a little date string?
I used the old function all the way accross my code so a way to just keep the old use (with only one then) would be awesome.
As always thanks all.
You would write it that way:
get_data: function(url) {
return get_timestamp() // request the timestamp this returns a promise
.then(function(timestamp) { // on which then is called wich itself returns a promise.
// the callback of this then is called as soon
// as the promise returned by timestamp
// is resolved
return $http({
method: "GET",
url: url
headers: {
'timestamp': timestamp,
}
}) // here you return the Promise that is created by the $http
})
.then(function(response) { // the callback of this then is called as soon
// as the previous promise was resolved
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
})
.catch(function(response) {
console.dir(response);
return response;
});
},
First of all I would use:
.then(function(response) {
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
})
.catch(function(response) {
console.dir(response);
return response;
});
Instead of
.then(
function successCallback(response) {
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
},
function errorCallback(response) {
console.dir(response);
return response;
});
})
Because it is easier to read later if you have longer chains.
The return returns the last Promise that was created through the chain, the one that was returned by the call .catch(function(response) {...}
You should chain the Promises and return the result of the chain:
function get_data(url) {
return get_timestamp()
.then((timestamp) => {
return $http({
method: "GET",
url: url,
headers: {
timestamp: timestamp
}
});
})
.then((response) => {
console.dir("Response:");
console.dir(response["data"]);
return response["data"];
})
.catch((response) => {
console.dir(response);
return response;
});
}
Note that we only need one .catch at the end of the chain to catch all exceptions.
Related
Im trying to execute a function after the other one in Vue.js. I've already tried async/await, callback functions, .then, but it somehow doesnt want to load one after the other. What is a possible solution?
auth_mixin.js:
async auth () {
console.log("authban")
var token = this.getCookie("token")
var jsonData = {}
jsonData["token"] = token
console.log(jsonData)
var bodyFormData = new FormData();
bodyFormData.append('data', JSON.stringify(jsonData));
axios({
method: 'post',
url: 'backend/index.php?action=checkAuth',
data: bodyFormData,
headers: {'Content-Type': 'multipart/form-data'}
})
.then(function (response) {
console.log(response);
if(response.data.status==="OK"){
console.log("ok")
return true;
}else{
console.log("nem ok")
return false;
}
})
.catch(function (response) {
console.log(response);
return false;
});
}
Navbar.vue:
created () {
var result=false
this.auth().then(this.checkIfLoggedIn(result))
},
methods: {
checkIfLoggedIn (isLoggedIn) {
console.log("na ez lesz az erdekes "+isLoggedIn)
if(isLoggedIn === true){
console.log("true")
document.getElementById("logged_out").style.display="none";
document.getElementById("logged_in").style.display="block";
}else{
console.log("fail");
}
}
}
this.auth().then(this.checkIfLoggedIn(result))
You have two problems.
First: this.checkIfLoggedIn(result) calls checkIfLoggedIn immediately. You need to pass a function to then.
this.auth().then(() => this.checkIfLoggedIn(result))
Second: With that change, you call checkIfLoggedIn when auth resolves.
So when does auth resolve? Well, it is defined with the async keyword, so it resolves when it returns (unless it returns a promise, in which case it adopts that promise instead).
So what does it return? It has no return statement, so it returns undefined when it gets to the end … which is immediately after the call to axios (since you aren't awaiting that).
If you returned the return value of axios(...).etc then it wouldn't resolve until that promise resolved.
(Aside: You're using async, you should probably refactor to use await, try {} catch() {} instead of .then() and .catch()).
Having an angular service that returns promise, is it possible to detect whether a consumer of this promise handles error ? I'd like to provide a default error handling in service, but ensure that it would be used only if no error handler is defined down the execution chain.
The service method looks like this:
function serviceMethod(method, url, data)
{
return $http({
method: method,
url: url,
data: data
})
.then(
function (response) {
return response;
},
function (response) {
console.log('ERROR!'); // default error handling
}
);
}
The serviceMethod returns a promise, therefore:
1) If the consumer provides error handler, the error should be handled exclusively by it.
$scope.getResponse = function () {
return Services.serviceMethod('put', $scope.url, $scope.someData)
.then(function (response) {
}, function (error) {
// Custom error handling.
});
}
2) If the consumer doesn't provide handler, the error should be handled exclusively by service handler.
Is it possible to achieve in the first successor of serviceMethod? Is it possible at any point in the chain (the error is handled exclusively by the first consumer to provide error handler)?
You have the answer in the code you haven given. Do it like this:
function serviceMethod(method, url, data)
{
return $http({
method: method,
url: url,
data: data
})
.then(
function (response) {
return response;
},
function (response) {
return response; // default error handling
}
);
}
And your getResponse method:
$scope.getResponse = function () {
return Services.serviceMethod('put', $scope.url, $scope.someData)
.then(function (response) {
}, function (error) {
alert(error.code); //Default error handling returned from error function in serviceMethod
alert("My custom error"); //Custom error handling
});
}
It is very important that the rejection handler in the service throw the error response. Otherwise the $q service will convert the rejected promise to a successful response.
function serviceMethod(method, url, data)
{
return $http({
method: method,
url: url,
data: data
})
.then(
function (response) {
return response;
},
function (errorResponse) {
//return response; // default error handling
throw errorResponse;
//OR
//return $q.reject(errorResponse);
}
);
}
A common problem is erroneous conversion of rejected promises to fulfilled promises by failing to return anything. When a function omits a return statement, the function returns a value of undefined. In that case the $q service will convert a rejected promise to a fulfilled promise that resolves with a value of undefined.
That said. No, it is not possible for a service to know how a consumer will use a rejected promise. If a consumer wants a service to skip default error handling, the consumer must specify that in the service call:
function serviceMethod(method, url, data, skipErrorHandling)
{
return $http({
method: method,
url: url,
data: data
})
.then(function (response) {
return response.data;
})
.catch(function (errorResponse) {
if (skipErrorHandling)
throw errorResponse;
}
//Put error handler here
//Correct error
var promise = retry(method, url, data);
return promise;
);
}
I have a request function :
function search(request) {
return $http.post('/path/to/resource', request);
}
I can call it like this :
search({/*...*/})
.success(function() {})
.error(function() {})
As I often need to find some objects by their ID, I need a shortcut function. I cannot find how to create this function so that I can also chain it with success() and error() functions.
I searched how to create a promise in angular and found the documentation about $q and this is what I tried :
function searchById(id) {
var deferred = $q.defer();
search({id: id}).
then(function (response) {
deferred.resolve(response.data.results[0]);
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
}
I can only call it like this :
searchById().then(successCallback, errorCallback);
I would like to be able to call it like this :
searchById()
.success(successCallback)
.error(errorCallback);
The documentation about $q indicates that it returns a promise whereas the documentation about $http indicates that it returns an httpPromise but I cannot figure how to create an httpPromise.
Any idea?
In angular promises the error callback should be catch not error, try this
searchById()
.then(successCallback)
.catch(errorCallback);
sjokkogutten is correct that you don't need to use $q in this case you can simplify this
var deferred = $q.defer();
search({id: id}).
then(function (response) {
deferred.resolve(response.data.results[0]);
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
to this
return search({id: id}).
then(function (response) {
return response.data.results[0];
}
$http is already returning a promise, so there is no need to use $q.defer(). Also, success() and error() has been depreciated (since 1.4.4), you should use then() instead.
Call your function like this:
search(request).then(function(data){
// successcallback
}, function(error){
// errorcallback
})
Creating a factory with $http functions will allow you to use .success and .error. But you really should be using .then.
app.factory("dataService", function($http) {
return {
search: function() {
return $http.get('path/to/api');
},
searchById: function(payload) {
return $http.post('path/to/api', payload);
},
searchMoreThings: function(payload) {
if(payload === "foo") payload = "bar";
return $http.post('path/to/api', payload);
}
}
});
You can do:
dataService.searchById(searchTerm).success().error();
Here is an actual example :
app.controller('controller',function($scope,$rootScope,$http,){
$scope.login = function(request){
var promise = $http.post(/path/to/resource, request, {
headers: {
'Content-Type': 'application/json'
}
}).then(function success(res){
//it worked, you have data in res
},function error(res){
// it failed
});
};
});
I have a Cloud Code function that will execute n times the same block. The block consist in an http call with auth header. to make things simple, I have created a function at the root of my main.js. The function needs to return a result and keep in memory the authData (in order to reuse it for future calls).
function requestURI (uri){
var authData; // gets generated if null, should be reused if not null
var result; // supposingly contains the results
return something();
}
The function something() is a Parse.Promise because I need my calls to be asynchronous. As I understand I can't attach the result nor the authData to my promise.... If I run a console.log() within the function requestURI(), I see authData and result correctly populated as expected
Then I want this function from a Parse function. (the whole purpose is to have the function being re-usable by any other)
Parse.Cloud.define("testCall", function(request, response) {
var uri1 = '...';
var uri2 = '...';
var uri3 = '...';
return requestURI(uri1).then(function(){
// how do I get the result of my request?
return request(uri2);
}).then(function(){
// how do I get the result of my request?
return request(uri3);
});
}
The problem I have is that I can't retrieve my result out of the requestURI function and it seems that authData is reset everytime I run the function
I read the solution lies in closures but I can't get my head around them...
edit: add the function something():
return Parse.Cloud.httpRequest({
method: 'GET',
url: url,
headers: {
"Authorization" : digestAuthHeader
},
success: function(httpResponse) {
// all went well, let's increase the nonceCount, for future calls
authData["nc"] += 1;
// I need to return the result object in a promise
result = httpResponse.data;
// return a Promise that can be handled by any function
return Parse.Promise.as(result)); // this promise doesn't work
},
error: function(httpResponse) {
console.error('Request failed with response code ' + httpResponse.status);
return (null,Parse.Promise.error(httpResponse.text));
}
});
edit: here is what I'm trying
// authData is not null, we can make an authenticated call
function makeAuthenticatedRequest(){
// generate the appropriate auth Header;
var digestAuthHeader = generateDigestAuthHeader();
return Parse.Cloud.httpRequest({
method: 'GET',
url: url,
headers: {
"Authorization" : digestAuthHeader
}}).then(function(httpResponse) {
// all went well, let's increase the nonceCount, for future calls
authData["nc"] += 1;
// create the final object to return in a promise
result = httpResponse.data;
console.log(result) // returns something not null!!!
// return a Promise that can be handled by any function
return promise.resolve({'authData': authData, 'result': result});
},
function(error) {
console.error('Request failed with response code ' + error.status);
return (null,Parse.Promise.error(error));
});
}
Parse.Cloud.define("testCall", function(request, response) {
var uri1 = '...';
var authData;
return apiCall1001Menus(authData,uri1).then(function(result){
response.success(result); // returns {}
});
});
my response callback is {}!!! which is not what I would expect at all
I will give you an example to prevent my bad english cause misleading.
The following rewrite function makeAuthenticatedRequest() in callback deferred antipattern and promise fashion.
Callback:
Deferred antipattern:
function makeAuthenticatedRequest(){
// generate the appropriate auth Header;
var digestAuthHeader = generateDigestAuthHeader();
var promise = new Parse.promise();
Parse.Cloud.httpRequest({
method: 'GET',
url: url,
headers: {
"Authorization" : digestAuthHeader
},
success: function(httpResponse) {
// all went well, let's increase the nonceCount, for future calls
authData["nc"] += 1;
// create the final object to return in a promise
result = httpResponse.data;
console.log(result) // returns something not null!!!
// return a Promise that can be handled by any function
promise.resolve({'authData': authData, 'result': result});
},
error: function(error) {
console.error('Request failed with response code ' + error.status);
// it could be promise.resolve (success) or promise.reject (error)
promise.reject(error);
}
});
return promise;
}
Promise:
function makeAuthenticatedRequest(){
// generate the appropriate auth Header;
var digestAuthHeader = generateDigestAuthHeader();
return Parse.Cloud.httpRequest({
method: 'GET',
url: url,
headers: {
"Authorization" : digestAuthHeader
}
}).then(function(httpResponse) {
// all went well, let's increase the nonceCount, for future calls
authData["nc"] += 1;
// create the final object to return in a promise
result = httpResponse.data;
console.log(result) // returns something not null!!!
// return a Promise that can be handled by any function
return {'authData': authData, 'result': result};
}, function(error) {
console.error('Request failed with response code ' + error.status);
return Parse.Promise.error(error);
});
}
I want to create a cache for my http requests, so I want to duplicate the $http callbacks.
this is my function:
function getData() {
if(cacheExists()) {
return cache;
}
return $http({
method: 'POST',
url: 'http://something.com',
params: {something}
});
}
and this is how I handle it:
getData()
.success(function(data) {
$scope.spot = data.data;
console.log($scope.spot);
}).error(function(data) {
console.log('error');
});
this will work perfectly with angularjs $http method,
but will not work with my "cache", becuase the "cache" should have those callbacks: success & error, how do I create them?
Its because the $http returns a promise. You can solve this by using the $q service and return the cache as a promise.
//inject $q
function getData() {
var deffered = $q.defer()
if(cacheExists()) {
deffered.resolve(cache);
} else {
$http({
method: 'POST',
url: 'http://something.com',
params: {something}
})
.success(function(data) {
deffered.resolve(data);
})
.error(function(response) {
deffered.reject(response);
})
}
return deffered.promise;
}
So what's happening here that you are creating "deffered" as a promise. A Promise is basically a way to handle async tasks. And when you get a promise you need to resolve it, as you did with your return value from the http call. But with a $q deffered you are using 'then' instead of 'success'. Consider the code snippet below:
getData()
.then(function(data) {
$scope.spot = data.data;
console.log($scope.spot);
})
Hope this helps.
UPDATE
As of you example by handling errors you could do this:
getData()
.then(function(data) {
if(data.data) {
$scope.spot = data.data;
console.log($scope.spot);
} else {
console.log("its an err");
}
});
Or this
getData()
.then(successCallback, errorCallback);
function successCallback(data) {
$scope.spot = data.data;
console.log($scope.spot);
}
function errorCallback() {
console.log("its an err");
}