I have the code below which is in a Vue script.
user_id = 100; //sample data
$.ajax({
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
type: "GET",
url: '/user',
success: function (user) {
user_id = user.user_id;
console.log(user_id); //returns 1
},
error: function (result) {
}
});
console.log(user_id); //returns 100 not 1
I want to be able to store the value that is resulted from the ajax request which is 1. However, when I console.log at the end of the script it returns 100 not 1. I think that I need to use a promise/callback to solve this but I am not sure how/what I need to do. Can someone help me?
Define your method and return as a promise.
function getUsers() {
return new Promise((resolve, reject) => {
$.ajax({
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
type: "GET",
url: '/user',
success: function (data) {
resolve(data);
},
error: function (error) {
reject(error);
}
});
});
}
You would call the method as below.
getUsers().then((data) => {
console.log(data); /* you will get the new data returned from ajax.*/
}).catch((error) => {
console.log(error);
});
This is how you can promisify callbacks in general:
let doXWithCallback = callback => {
// do x...
callback();
};
let doXPromisified = () => new Promise(doXWithCallback);
doXWithCallback(() => console.log('do x with callback'));
doXPromisified().then(() => console.log('do x promisified'));
For your example specifically:
let doRequest = () =>
new Promise((resolve, reject) =>
$.ajax({
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
type: "GET",
url: '/user',
success: user => resolve(user.user_id),
error: reject(),
}));
doRequest.then(userId => console.log('userId is', userId));
Related
I'm send a post request and I get the response like this
" [Symbol(Response internals)]: {
url: 'https://login.somenewloginpage'}"
and what I want to do is I want to open a new page via that url but it does not direct to the new page.
const login= () => async () => {
const api = `somePostRequest`
fetch(api, {
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-url-encoded',
Accept: 'application/json',
},
})
.then(function(res) {
return res //maybe I should do something in this part...
})
.then(data => console.log(data));
};
Here's how to use fetch() with async/await syntax :
const login= () => async () => {
const api = `somePostRequest`;
const response = await fetch(api, {
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-url-encoded',
Accept: 'application/json',
},
});
const data = await response.json(); // { url: 'https://login.somenewloginpage'}
window.location.replace(data.url); // <-- Redirection
};
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I have this axios call with a nested axios call that causes async problems. Here's a simplifyed snippet of my code:
axios.all([
axios.get('https://foo..', {
headers: { 'Content-Type': 'application/json' },
data: { 'endpoint': 'v2/bar..' },
}),
]).then(axios.spread((resp) => {
var resp = resp.data.content;
var items = resp.items.slice(0, 1);
items.forEach(item => {
var dataUrl = item.url.split('/v2/').slice(1).join('');
axios.get('https://foo..', {
headers: { 'Content-Type': 'application/json' },
data: { 'endpoint': 'v2/' + dataUrl + 'bar..' }
}).then(resp => {
const data = resp.data.content;
// alot of code goes here
}
)
}),
axios.get('https://foo..' + pageNum.toString(), {
headers: { 'Content-Type': 'application/json' },
data: {}
}
).then(resp => {
fillTemplate(resp.data.content[0].xml);
}
).catch(function (error) { console.log("DB-GET-Error:\n" + error) });
})
).catch(function (error) { console.log("error " + error) });
The problem is that 'fillTemplate()' executes before 'items.forEach(...)' is done and can populate 'fillTemplate()' with data, how can I solve this?
Problem
Both requests are send async and second won't wait for first one. Solution is to use async/await or Promise.all().
Escaping the callback hell
You are still having the problem of deeply nested code, which becomes unreadable. Below are two examples, first just fixing the issue and second with better readability.
Sample Code
axios.all([
axios.get('https://foo..', {
headers: { 'Content-Type': 'application/json' },
data: { 'endpoint': 'v2/bar..' },
}),
]).then(axios.spread((resp) => {
// cache axios promises here
var promises = []
var resp = resp.data.content;
var items = resp.items.slice(0, 1);
items.forEach(item => {
var dataUrl = item.url.split('/v2/').slice(1).join('');
var promise = axios.get('https://foo..', {
headers: { 'Content-Type': 'application/json' },
data: { 'endpoint': 'v2/' + dataUrl + 'bar..' }
}).then(resp => {
const data = resp.data.content;
// alot of code goes here
});
// add promises to array
promises.push(promise);
});
// wait for all promises to execute next request
Promise.all(promises).then((results) => {
axios.get('https://foo..' + pageNum.toString(), {
headers: { 'Content-Type': 'application/json' },
data: {}
}).then(resp => {
fillTemplate(resp.data.content[0].xml);
}).catch(function (error) { console.log("DB-GET-Error:\n" + error) });
});
})).catch(function (error) { console.log("error " + error) });
Refactored version
axios.all([doStuff()]).then(axios.spread((resp) => {
var promises = []
var resp = resp.data.content;
var items = resp.items.slice(0, 1);
items.forEach(item => {
var dataUrl = item.url.split('/v2/').slice(1).join('');
var promise = doOtherStuff();
promises.push(promise);
});
Promise.all(promises).then(finalCallback).then(resp => {
fillTemplate(resp.data.content[0].xml);
});
}));
function doStuff() {
return axios.get('https://foo..', {
headers: { 'Content-Type': 'application/json' },
data: { 'endpoint': 'v2/bar..' },
});
}
function doOtherStuff() {
return axios.get('https://foo..', {
headers: { 'Content-Type': 'application/json' },
data: { 'endpoint': 'v2/' + dataUrl + 'bar..' }
}
}
function finalCallback(results) {
return axios.get('https://foo..' + pageNum.toString(), {
headers: { 'Content-Type': 'application/json' },
data: {}
});
}
I have this arrow function:
saveNewPermissions = (newGroupPermissions, groupName) => {
fetch(this.baseUrl + "/addPermission/group/" + groupName, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({
permissions: newGroupPermissions
})
}).then(response => response.json())
.then(json => {
console.log(JSON.stringify(json))
return json;
});
}
That function above is on the Service file, and on my component class i want to get the json that the function returns:
this.Service.save(newList, groupName)
I tried to do this but it didnt work:
this.uaaService.saveNewPermissions(newList, groupName).then(response=>{
console.log(response)
})
First of, you would want to return the Promise from the arrow function
saveNewPermissions = (newGroupPermissions, groupName) => {
return fetch(...) // returns a Promise object
}
then if you expect newList to be an array, you want to do:
this.uaaService.saveNewPermissions(newGroupPermissions, groupName).then((newListJSON) => {
this.Service.save(newListJSON, groupName);
})
(Working in the Postman environment) it was detected that the following code runs through all the url requests without printing the result.
_.forEach(
urls,
function (myUrl) {
pm.sendRequest({
url: myUrl,
method: 'GET',
header: {
'content-type': 'application/json'
}
}, function (err, res) {
console.log(res)
});
}
)
pm.environment.set(`sections`,sections);
}
Is there any way to provide a callback within forEach, might appear something like given below code. Or is there any alternative to it.
function callback() {
console.log('callback');
}
_.forEach(
urls,
function (myUrl,callback) {
pm.sendRequest({
url: myUrl,
method: 'GET',
header: {
'content-type': 'application/json'
}
}, function (err, res) {
console.log(res)
});
}
)
pm.environment.set(`sections`,sections);
}
I had initially thought that the response handling function at the end would take care of that but it doesn't.
Found it better to branch out the logic into another function getUrl and pass the callback into it.
Following code works:
_.forEach (
urls,
function (myUrl) {
getUrl(myUrl,function (err,res) {
if (err) {
console.log(err);
} else {
console.log(JSON.stringify(res));
}
});
}
);
function getUrl(myUrl, callback) {
var call = {
url: myUrl,
method: 'GET',
header: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
}
};
pm.sendRequest(
call,
function (err, res) {
callback (err,res);
}
);
}
I have a method called fetchMerchantData which calls 3 other async methods. I'm trying to use Promise so that it doesn't call resp.direct(301, ...) until all the requests are finished but it's not working.
function fetchOauth2Token(authorizationCode, resp) {
...
request({
url: `https://connect.squareup.com/oauth2/token`,
method: "POST",
json: true,
headers: oauthRequestHeaders,
form: oauthRequestBody,
}, (error, oauthResp, body) => {
if (body.access_token) {
Promise.resolve(fetchMerchantData(body.access_token, body.merchant_id)).then(() => {
console.log("last!"); //<--------------------- this is printing first
resp.redirect(
301,
`myurl.com/blah`
);
});
;
} else {
// TODO find out what to do on failure
resp.redirect(301, `myurl.com/?error=true`);
}
})
}
function fetchMerchantData(access_token, merchant_id){
const updates = {};
request({
url: `https://connect.squareup.com/v1/me/locations`,
method: "GET",
json: true,
headers: {
Authorization: `Bearer ${access_token}`,
Accept: 'application/json',
"Content-Type": "application/json",
},
}, (error, response) => {
if (!error) {
const locations = response.body;
Promise.all([
saveMerchant(merchant_id, access_token, locations),
saveLocations(merchant_id, locations),
installWebhookForLocations(access_token, locations),
]).then(values => {
console.log("first!"); //<--------------------- this is printing last
return Promise.resolve("Success");
})
}
});
}
And here's an example of the saveMerchant method which calls firebase:
function saveMerchant(merchant_id, access_token, locations) {
const merchantRef = database.ref('merchants').child(merchant_id);
const location_ids = locations.map(location => location.id);
merchantRef.update({
access_token,
location_ids,
});
}
How would I synchronize this?
== UPDATE ==
This is how my installWebhookForLocations method looks:
function installWebhookForLocations(access_token, locations){
const locationIds = locations.map(location => location.id);
locationIds.forEach((locationId) => {
request({
url: `https://connect.squareup.com/v1/${locationId}/webhooks`,
method: "PUT",
json: true,
headers: {
Authorization: `Bearer ${access_token}`,
Accept: 'application/json',
"Content-Type": "application/json",
},
body: ["PAYMENT_UPDATED"],
}, (error) => {
if (!error){
console.log(`Webhook installed for ${locationId}`);
}
});
});
}
Here is an example of saveMerchant that would use a promise.
function saveMerchant(merchant_id, access_token, locations) {
return new Promise(function (resolve, reject) {
const merchantRef = database.ref('merchants').child(merchant_id);
const location_ids = locations.map(location => location.id);
merchantRef.update({
access_token,
location_ids,
}, function (error) {
if (error) return reject(error);
resolve();
});
});
}
To make the above easier, there is a nice Promise library called Bluebird, it has a promisify utility, that you could apply to firebird update method.
Also for your second question were your using forEach, bluebird has a nice utility function called map that you could use instead.