I have the following common code to invoke AJAX requests. My question is if the "return" keyword is necessary for the $.ajax (since doing $.ajax would anyways return a promise) OR if it is for some other purpose ?
doXhr: function(postData, requestUrl){
var promise = new Ember.RSVP.Promise(function(resolve, reject) {
return $.ajax({ //Is the "return" required on this line ?
url: requestUrl,
})
.done(function(response, status, xhrObject) {
resolve(response);
})
.fail(function(xhrObject, status, error){
reject(errorObj);
});
})
return promise;
},
just like other promise libraries (Native, Bluebird, etc), the resolve, reject functions are actually callbacks, so there is no need for a return inside the new Promise. (If you never call resolve or reject though, your promise will be pending forever).
The only return that is needed in the return of the RSVP.Promise like what you have at the bottom -- though only if you are awaiting or thening doXhr
Also, neat side-tip:
With the the more recent ember, you can just do:
import { Promise } from 'rsvp';
new Promise((resolve, reject) => {
// stuff
});
Edit
for how the function body of the promise get's executed, you'll need to look at the constructor of the Promise:
https://github.com/tildeio/rsvp.js/blob/master/lib/rsvp/promise.js#L143
the resolver function (the function you define when making a new promise) is passed to here https://github.com/tildeio/rsvp.js/blob/master/lib/rsvp/-internal.js#L231 and then evaluated.
Hope this helps!
Related
I've this quick doubt that might be related to promise chaining but I'm not able to think about it clearly.
So basically, I've this function(myFn) that returns axios.request() which basically returns a promise, so i can now do myFn.then().catch() and it works, now what I want to do is to create a promise wrapper on top of that, which will resolve only when response.data.status =="somevalue", here response.data means the request was successful and a result came but now i want to resolve myFn only when response.data.status =="somevalue".
Since your function returns a promise, you can just use the promise's result in the .then callback. If the response.data.status isn't what you want, throw an error, otherwise return the response, thus resolving the promise:
myFn().then(response => {
if (response.data.status !== "somevalue") {
throw new Error("invalid status");
}
return response;
})
Edit: If you want to wrap this in a custom Promise, you could do something like this:
const myPromise = new Promise((resolve, reject)=>{
myFn().then(response => {
if (response.data.status !== "somevalue") {
reject("Invalid status");}
else{
resolve("somevalue");}
});
});
And then call this promise with callback functions for the fulfilled and rejected cases of the promise:
myPromise.then(handleSucess, handleFailure);
For reference see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
You can try if this works for you. I am not sure though, if the throw here works as expected.
const axiosInstance = axios.create({
transformResponse: [function (data) {
if (data.status === "somevalue") {
return data;
}
throw new Error(`wrong status: ${data.status}`);
}],
});
Then use this axiosInstance to do your calls.
If this does not work, you could also give the axios response interceptors a try. How can you use axios interceptors.
I am chaining two calls to a function that uses fetch and returns a promise.
The calls sometimes arrive in the wrong order, reversed.
I assume my function code is wrong, derived from a bad understanding of either promises, fetch, or both.
This is the function code, simplified:
function shop(id) {
return new Promise(function(resolve, reject) {
fetch('/shop', {
method: 'post',
body: JSON.stringify({id: id}) ,
headers: {'Content-type': 'application/json '}
}).then(function (response) {
return response.json();
}).then(function (data) {
console.log(data);
resolve(data);
})
})
}
I chain the function in this manner:
shop('potatoe')
.then(shop('mushrooms'));
As explained, these two sometimes arrive in the wrong order, with mushrooms being shopped before potatoes.
May I have my returns or resolve() wrongly written in the shop() function?
Thank you for your help.
The main issue is that you call shop('mushrooms') immediately, synchronously. You didn't pass a callback to the then method, but instead executed what would be the intended callback. So change the main code to:
shop('potatoe')
.then(() => shop('mushrooms'));
This solves your problem.
However, you are creating a useless promise in your function. The fetch().then().then() chain already returns a promise; you don't need to create yet another one. Read about the promise constructor antipattern.
So remove this new Promise wrapper, and remove the final .then() call which was only there to call resolve:
function shop(id) {
// return the promise returned by this promise chain:
return fetch('/shop', {
method: 'post',
body: JSON.stringify({id: id}) ,
headers: {'Content-type': 'application/json '}
}).then(function (response) {
return response.json();
});
}
In my Controller:
function login(credentials) {
AuthService
.login(credentials)
.then(successCallback, errorCallback);
//same issue with .then(successCallback).catch(errorCallback);
}
function successCallback() {
// do something after success
}
function errorCallback(data) {
// do something after error
}
and in my AuthService:
authService.login = function (credentials) {
return $http
.post(ENV.apiEndpoint + 'api/v1/login_check', credentials)
.then(
function (result) {
Session.create(result.data.token, result.data.data);
},
function (data) {
Messages.create('Login failed: ' + data.statusText);
}
);
}
When my POST delivers a 200 response code, everything works as expected do something after success is executed.
But when my POST results e.g. in a 401 I can see that Messages.create is called (so in this case it enters the error path), but unfortunately my Controller calls the successCallback and not the errorCallback.
I had to migrate this because I was using the deprecated and since Angular 1.6 removed .success and .error promise attributes. It was working back then, but after migration this doesn't work anymore.
What am I doing wrong here?
You may reject the promise in your error callback.
authService.login = function (credentials) {
return $http
.post(ENV.apiEndpoint + 'api/v1/login_check', credentials)
.then(
function (result) {
Session.create(result.data.token, result.data.data);
},
function (data) {
Messages.create('Login failed: ' + data.statusText);
return $q.reject(data);
}
);
}
From Angular $q doc:
reject(reason);
Creates a promise that is resolved as rejected with the specified
reason. This api should be used to forward rejection in a chain of
promises. If you are dealing with the last promise in a promise chain,
you don't need to worry about it.
When comparing deferreds/promises to the familiar behavior of
try/catch/throw, think of reject as the throw keyword in JavaScript.
This also means that if you "catch" an error via a promise error
callback and you want to forward the error to the promise derived from
the current promise, you have to "rethrow" the error by returning a
rejection constructed via reject.
I have the following function:
function getRandomTweetFromLocation(geocode) {
return new Promise(function (resolve, reject) {
var count = 50;
T.get('search/tweets', {
q: geocode,
count: count
}, function (err, data, response) {
instead of passing that anonymous function as a callback, I wanted to pass another function that would receive err, data, response, but it also needs to receive resolve, reject in order to resolve the promise. I've written the function like this:
function chooseAGoodTweet(err, data, response, resolve, reject) {
but if I pass its name as the callback, it'll only receive err, data, response. Is there a way to pass it as a callback but make resolve and reject go together?
First off, you need to wrap just the T.get() in a promise wrapper and then use that promise wrapper to get the results of the function.
Secondly, promises ONLY ever resolve with a single argument so if you want to pass more than one argument to them, you put those multiple arguments into an object and resolve with the object. Here's a way you could do that:
// make promise-returning version of T.get()
function tGetAsync(geocode, cnt) {
return new Promise(function (resolve, reject) {
T.get('search/tweets', {q: geocode, count: cnt}, function (err, data, response) {
if (err) return reject(err);
// put multiple results into an object and resolve with that single object
resolve({data: data, response: response});
});
});
}
The, you can use that like this:
tGetAsync(someGeoCode, someCnt).then(function(result) {
console.log(result.data);
console.log(result.response);
}).catch(function(err) {
console.err(err);
});
You don't show what chooseAGoodTweet() is supposed to do, but it can use tGetAsync() like this:
function chooseAGoodTweet(geocode) {
return tGetAsync(geoCode, 50).then(function(result) {
// do some processing of result.data or result.response
// and return a value here
return someTweet;
});
}
Then, you would use it as:
chooseAGoodTweet(someGeoCode).then(function(tweet) {
// process chosen tweet here
}).catch(function(err) {
// handle error here
});
Is there a way to pass it as a callback but make resolve and reject go together?
You can't change what a callback will be passed from outside the function that calls the callback. There is simply no way to do that. You can either edit the function that calls the callback to change what it passes to the callback or you can insert a stub callback that gets the expected callback arguments and then adds some more to call your callback.
But, in this case, it appears you're mostly just misunderstanding how promises work so you need to get that right first and then you will have the proper arguments.
I am programming a new promise, it has many different conditions that call reject() or resolve() related to their state, also I know that the promise state will set with the first call to reject() | resolve().
My question is:
Is there any native (build-in) way to get the promise state?
The following is a demonstrative-code:
exports.addStatement = function (db, description, data) {
return new Promise(function (resolve, reject) {
validator.validateStatement(description, data)
.then(function (data) {
//......
if(cnd1)
resolve(res);
if(cnd2)
reject(err);
//......
//How to check if this promise is rejected or resolved yet?
})
.catch(function (err) {
reject(err);
})
})
};
You cannot directly examine the state of a promise. That's not how they work. You can use .then() or .catch() on them with a callback to get notified.
Or, in your specific case, you can probably change the way your code is structured to remove the anti-pattern of creating an unnecessary outer promise and switching your logic to if/else if/else.
Here's the cleaned up code:
exports.addStatement = function (db, description, data) {
return validator.validateStatement(description, data)
.then(function (data) {
//......
if(cnd1) {
// make res be the resolved value of the promise
return res;
} else if(cnd2) {
// make promise become rejected with err as the reason
throw err;
} else {
// decide what else to do here
}
})
})
};
If you couldn't make an if/else work for you, the above structure should still work because both the return and the throw terminate the execution of the .then() handler. So, the only code that continues after them is code that has not yet set the resolved/rejected value for the current promise so you don't have to look at the state of the promise to know that. If the code gets past the return and throw and is still executing, then neither of those was executed and the resolved/rejected value of the current promise is still unset.