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();
});
}
Related
I have an isolated scene. And there is a promise in a separate file. But my scene does not want to wait for an answer. And continue to work. And continues to run, how do I make it wait for an answer, and continued to work
file: a
async function apiExchangerate(country, amount) {
return new Promise(function (resolve, reject) {
axios.get(``, {
headers: { "Accept-Encoding": "gzip,deflate,compress" },
}).then(
(response) => {
var result = String(response.data.result).replace(/\..*/, '');
console.log('Processing Request');
resolve(result);
},
(error) => {
reject(error);
}
);
});
}
module.exports = apiExchangerate
file: b
let request_currency = await apiExchangerate(country,amount) // you have to wait for a response, and then continue the work
I want my function to wait for a response and continue executing the script. On the Internet, I have not found an answer to my question.
P.s it doesn't work - What is the explicit promise construction antipattern and how do I avoid it?
You're wrapping Promises in Promises for no real reason. One of the reasons why this is an anti-pattern is because it's very easy to get confused by that and to mis-handle resolving/rejecting those Promises.
(My guess is that somewhere you're returning a Promise which resolves to... a Promise. And you're not double-awaiting it so you never resolve the actual Promise.)
Don't over-design it. Simplify the function. axios.get already returns a Promise, and the function is already async, use those:
async function apiExchangerate(country, amount) {
let response = await axios.get(``, { headers: { "Accept-Encoding": "gzip,deflate,compress" } });
let result = String(response.data.result).replace(/\..*/, '');
console.log('Processing Request');
return result;
}
Then what you have is a simple async function which will internally await its own operations. And you can await that function:
let request_currency = await apiExchangerate(country, amount);
Is there a way I can write this block of code without all the repeated code for calling axios?
export const handler = async (event, useAwait) => {
const { path, data } = JSON.parse(event);
if (useAwait) {
return await axios(`https://example.com/api${path}`, {
method: 'post',
headers: {
'api-key': key,
},
data: data,
});
} else {
// non-blocking
axios(`https://example.com/api${path}`, {
method: 'post',
headers: {
'api-key': key,
},
data: data,
});
return true;
}
};
Put the promise into a variable, and then you can conditionally return it.
export const handler = async (event, useAwait) => {
const { data } = JSON.parse(event);
const prom = axios(`https://example.com/api${url}`, {
method: 'post',
headers: {
'api-key': key,
},
data: data,
});
return useAwait ? (await prom) : true;
};
That said, you may as well return the Promise itself - awaiting something that you're returning immediately doesn't help since you're not immediately inside a try.
return useAwait ? prom : true;
But calling this function without returning the result looks like a bad idea because then you may get an unhandled rejection. You may wish to add a .catch to the Promise in that case to avoid that.
export const handler = (event) => {
const { path, data } = JSON.parse(event);
const prom = axios(`https://example.com/api${path}`, {
method: 'post',
headers: {
'api-key': key,
},
data: data,
});
prom.catch(()=>{}); // prevent uncaught rejection
return prom;
}
If you don't test the return value of handler to be strictly equal to true, calling code can rely on promise objects being truthy in a conditional expression as needed, and the logic can be simplified to an ordinary function that returns a promise without using await or taking a useAwait parameter.
The dummy catch handler added to prom is to prevent generating an uncaught-rejected-promise error if the caller doesn't handle rejection of the axios promise, and can be omitted if uncaught rejection errors are acceptable. How calling code is meant to operate without waiting for the axios call completed is unclear and not covered here.
One of the reasons for suggesting major simplifications is the // non-blocking comment in the posted code. Async functions always return a promise object when called with no blocking effect, either to continued execution of calling code or browser operation.
The only effective difference between using await or not (in the post) is the value used to resolve the returned promise by handler.
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!
This question already has answers here:
What is the explicit promise construction antipattern and how do I avoid it?
(3 answers)
Closed 3 years ago.
There is a funcion returning a promise:
req.postP = function () {
var $promise = $.ajax({
url: self._url(url),
type: "POST",
data: JSON.stringify(data),
headers: headers,
contentType: self.JSON_CONTENT_TYPE,
dataType: "json"
});
return new Promise( (resolve, reject) => {
return $promise.then(resolve, reject);
} );
}
And another function that calls the first function:
req
.postP(POST_REGISTER, userData)
.then((data) => {
// is never ecexuted
})
.catch((err) => {
console.log(typeof err.then === "function"); // true
});
The then() function is never executed.
It seems that the promise is inside the catch function as err since err.then is a function.
The req.postP() function is called by other function as well and works in those cases.
The casting to a standard promise, was not the problem.
The problem was the dataType. According to the jQuery docs:
The type of data that you're expecting back from the server.
Since the server does not return any data for this request on success - but jQuery expected data - the promise was always rejected.
Related:
jQuery returning "parsererror" for ajax request
i've a function that return a boolean according to http.get call.
function checkValue (value) {
var defer = $q.defer();
var request = $http({
method: "get",
url: "someurl"
});
request.success(function (data) {
defer.resolve(data.response);
});
return defer.promise;
}
The problem is that return value is an object like this:
d {$$state: Object}
$$state: Object
status: 1
value: true
__proto__: Object
__proto__: d
How could i resolve it?
Yes, your function is returning a Promise object. The $$state property belongs to the promise object and is used by Angular internally (as suggested by $$ prefix) and therefore not to be used by developers using Angular. More interesting is that promise object has a "then" method which can be used to attach handlers that are called when the promise gets resolved. Promise gets resolved when the defer object related to the promise is resolved.
So, you could use your code as
checkValue.then(function (data) {
// This is called when the XHR response comes in and
// defer.resolve() is called in the checkValue function.
});
But, there is a better approach to this. Using promise chaining.
Which essentially removes the need to create a new defer object.
function checkValue (value) {
return $http({
method: "get",
url: "someurl"
}).then(function (data) {
return data.response;
});
}
Explaining everything about how Promises work is may be too much for this post. There are already awesome works done on that.
But, basically, $http already returns a promise. So you can just use this promise instead of creating a new one. The end usage of the function remains exactly the same as the snippet above.
BTW, .success() handlers are deprecated. So better start using .then() on your apps already.
Since you are using Promise, you need to use .then callback in order to get response or error:
function checkValue (value) {
var defer = $q.defer();
var request = $http({
method: "get",
url: "someurl"
});
request.success(function (data) {
defer.resolve(data.response);
});
return defer.promise;
}
var promise = checkValue('Hello World');
promise.then(function(response) {
//success
console.log(response);
}, function(reason) {
//failed
console.log(reason);
});
then(successCallback, errorCallback, notifyCallback) – regardless of
when the promise was or will be resolved or rejected, then calls one
of the success or error callbacks asynchronously as soon as the result
is available. The callbacks are called with a single argument: the
result or rejection reason. Additionally, the notify callback may be
called zero or more times to provide a progress indication, before the
promise is resolved or rejected.
But the optimized approach would be using $http's inbuilt promise instead of creating a new one.
var request = $http({
method: 'GET',
url: '/someUrl'
});
request.then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
You should try this,
function checkValue (value) {
return $q(resolve, reject) {
$http({
method: "get",
url: "someurl"
}).success(function (data) {
resolve(data.response);
}).then (function (error) {
reject(error);
});
}
}
This will resolve your data