Javascript Promise with Fetch results in "undefined" in implementation of Stripe API - javascript

I'm trying to implement the Stripe payment API in my website. However, I'm not very familiar with JavaScript, which is leading to some issues.
Most of my code is taken from Stripe's website, with some modifications made to better suit my own site.
I have a function CreateCustomer, which is intended to create a customer based on input from a form:
async function createCustomer () {
var emailJSON = {
"email": `${document.querySelector('#email-field').value}`
};
const output = await fetch('/create-customer', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(emailJSON)
})
.then((response) => {
return response.json();
})
.then((result) => {
return result;
});
return output;
}
This function is called by another function, and the output is stored because I have another function which requires the customer's Id.
var handleForm = function () {
// other code
const customerPromise = createCustomer();
var customer = customerPromise.data;
// more code
}
The fetch call in CreateCustomer calls the following C# code:
[Route ("create-customer")]
[ApiController]
public class CustomerCreationController : Controller
{
[HttpPost]
public ActionResult Create(CustomerCreateRequest request)
{
var customers = new CustomerService();
var customer = customers.Create(new CustomerCreateOptions
{
Email = request.Email,
});
return Json(new { _customer = customer.Id });
}
public class CustomerCreateRequest
{
[JsonProperty("email")]
public string? Email { get; set; }
}
}
From what I understand about Promises, if I call on the promise before it's resolved, I will get "undefined". But I also think I understand that if I await the promise (as I have done with the fetch in CreateCustomer), this shouldn't be an issue. However, whenever I run this code, I end up saving undefined in my customer variable in handleForm.
Like I said, I'm pretty unfamiliar with JavaScript, so the problem is probably some quirk of the language and/or Promises that I've overlooked. Thanks in advance for your help.

If your createCustomer is async, you need to await it, when calling it:
var handleForm = async function () {
// other code
const customerPromise = await createCustomer();
var customer = customerPromise.data;
// more code
}

#Marco is correct.
You also doing await and including .then witch is redundant.
Here are your options for promises:
// Using async/await
async function createCustomer () {
try {
const requestBody = {
email: `${document.querySelector('#email-field').value}`
};
const response = await fetch('/create-customer', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
})
const data = await response.json()
return data;
} catch (error) {
console.error(error);
}
}
/*
If you are calling createCustomer somewhere it has to
either be a async function to allow await, or
use .then((data) => console.log(data))
*/
const data = await createCustomer();
// Promise chains
async function createCustomer (callback) {
const requestBody = {
email: `${document.querySelector('#email-field').value}`
};
fetch('/create-customer', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
})
.then(response => response.json())
.then(data => callback(data))
.catch(e => console.error(e))
}
createCustomer((data) => {
// do something with data
})
One little thing with fetch is that you also have to await the .json() call. Since that is also a promise.
If you are doing a lot of network requests maybe take a look at axios.

Related

how to await value from another await variable

I cannot figure out how should I construct my code.
Basic info:
webhook (intercom) --> google cloud functions (await values) --> post message to slack.
Issue:
My code is working fine until I need to get an value from another await function and I am not sure how should I 'pause' the second await until the first one is complete.
Code:
// first function to get the information about agent
const getTeammateInfo = async function (teammate_id) {
try {
const response = await axios.get("https://api.intercom.io/admins/" + teammate_id, {
headers: {
'Authorization': "Bearer " + INTERCOM_API_AUTH_TOKEN,
'Content-type': "application/json",
'Accept': "application/json"
}
});
const { data } = response
return data
} catch (error) {
console.error(error);
}
};
// second function, which needs values from first function in order to find data
const slackID = async function (slack_email) {
if (slackID) {
try {
const response = await axios.get("https://api.intercom.io/admins/" + slack_email, {
headers: {
'Authorization': "Bearer " + SLACK_API_TOKEN,
'Content-type': "application/json",
'Accept': "application/json"
}
});
const { user } = response
return user
} catch (error) {
console.error(error);
}
}
};
It is used within the Google Cloud Function (
exports.execute = async (req, res) => {
try {
// map of req from intercom webhook
let {
data: {
item: {
conversation_rating: {
rating,
remark,
contact: {
id: customerId
},
teammate: {
id: teammateId
}
}
}
}
} = req.body
const teammateName = await getTeammateInfo(teammateId); // this works fine
const slackTeammateId = await slackID(teammateName.email) // this is where it fails I need to get the values from 'teammateName' in order for the function slackID to work
...
} catch (error) {
console.error(error)
res.status(500)
res.json({ error: error })
}
}
I have tried with Promise.all
const [teammateName, slackTeammateId] = await Promise.all([
getTeammateInfo(teammateId),
slackID(teammateName.email)
])
But I just cannot wrap my head around this how it should work.
Thank you.
// edit:
Code is okay, I just put the wrong API into the slackID function...
Thanks for double checking.
The problem with Promise.all() is that it fires both functions at the same time and waits until both are done, so you can't chain that way.
let teammateName
let slackTeammateId
getTeammateInfo(teammateId).then(r => {
teammateName = r
slackID(teammateName.email).then(r2 => {
slackTeammateId = r2
)}
);
then() method, on the other hand, waits until your method's return and then fires out everything in the callback function.

Use value from axios function in another function

I have this async function to get an API access token:
const getAccessToken = async () => {
try {
const body = new URLSearchParams({
grant_type: 'client_credentials',
scope: 'manage:all'
}).toString();
const config = {
headers: {
Content_Type: 'application/x-www-form-urlencoded'
},
auth: {
username: clientId,
password: clientSecret
}
};
const { data: res } = await axios.post(
`${baseUrl}/oauth2/token`,
body,
config
);
return res;
} catch (err) {
console.log(err);
}
};
It return a promise which I use in the following function to log the access token to the console:
getAccessToken().then(res => {
console.log(res.access_token);
});
It logs the token as a string.
Now I want to use this string value in another function in my code. Do I just call the function where I need the value like above and replace the console.log with return?
How do I use this string value in other parts of my code?
One way of doing it to call the function everywhere,
async function anotherFunction() {
const res = await getAccessToken();
console.log(res.access_token)
}
Another way of doing this is to call the function on your page load save the token in session/local storage/cookies and use the token in other functions by fetching it from session/local storage/cookies to avoid making multiple API calls.
On page load
const res = await getAccessToken();
localStorage.setItem('token', res.access_token);
async function anotherFunction() {
const res = localStorage.getItem('token');;
console.log(res.access_token)
}

Unable to extract resolved value from async function

I've created a function to get the current user's data from the Reddit API, as part of a Reddit object with multiple functions.
async getCurrentUserId() {
if (userID) return userID;
const token = await Reddit.getAccessToken().then(val => {
return val;
})
const url = "https://oauth.reddit.com/api/v1/me"
const headers = {
"Authorization": `Bearer ${token}`,
"User-Agent": "blablabla",
};
const response = await fetch(url, { headers: headers });
if (response.ok) {
const jsonResponse = await response.json();
return jsonResponse.name;
}
},
However, when I try and extract the data, I keep getting a promise rather than the resolved value, and I can't seem to be able to figure it out.
const userID = Reddit.getCurrentUserId().then(val => {
return val;
}) // returns "Promise {<pending>}"
Assistance with this would be appreciated.
You either need to do your logic inside .then(), or simplify by using await:
const token = await Reddit.getAccessToken();
...
const userID = await Reddit.getCurrentUserId();

then not waiting for Promise

I have this function using fetch
export async function login(email, password) {
let url = new URL("http://localhost:8080");
url.search = new URLSearchParams({
t: "login",
email: email,
password: password
});
fetch(url, {
method: "GET",
headers: new Headers()
})
.then(response => response.text())
.then(response => {
console.log(response);
return response;
})
.catch(err => console.log(err));
}
And the function that's supposed to call it and handle the result is this
handleSubmit() {
login(this.state.email, this.state.password).then(r => {
console.log(r);
if (String(r) === "true") this.props.updatePath("/main");
else this.setState({ current: 1 });
});
}
I'm new to javascript, and I'm still a bit confused with async thing.
In the console, the log from the second comes before the log from the first function, so I'm assuming it's not waiting for it to function.
The log on the first function spits out the correct result, I just can't get it to the other side.
I was using axios at first (had some problems it with) and this approch worked. Is there something different about fetch that stops this from working?
Any help would be appreciated
The Promise that comes from calling an async function resolves when that function returns. login internally doesn't await anything (that's probably a mistake). It returns before the internal fetch resolves. So while that fetch is in flight, your then callback in handleSubmit runs. Later the fetch initiated in login finishes and logs the actual response.
I would have you try writing login like this:
export async function login(email, password) {
let url = new URL("http://localhost:8080");
url.search = new URLSearchParams({
t: "login",
email: email,
password: password
});
try {
let response = await fetch(url, {
method: "GET",
headers: new Headers()
});
response = await response.text();
console.log(response);
return response;
} catch (err) {
console.log(err);
// original code had this issue where it doesn't reject on error nor resolve to anything useful
}
}
await is very simple concept - it just makes your function wait for the Promise in synchronous manner. In order to make use of it, put await before any expression that returns a Promise. In your case, you can return fetch() in your login() function and then just:
await login(this.state.email, this.state.password);
// execution continued when fetch is done

Get response from axios with await/async

I'm trying to get JSON object from axios
'use strict'
async function getData() {
try {
var ip = location.host;
await axios({
url: http() + ip + '/getData',
method: 'POST',
timeout: 8000,
headers: {
'Content-Type': 'application/json',
}
}).then(function (res) {
console.dir(res); // we are good here, the res has the JSON data
return res;
}).catch(function (err) {
console.error(err);
})
}
catch (err) {
console.error(err);
}
}
Now I need to fetch the res
let dataObj;
getData().then(function (result) {
console.dir(result); // Ooops, the result is undefined
dataObj = result;
});
The code is blocking and waits for the result, but I'm getting undefined instead of object
This seems to be one of those cases where async/await doesn't buy you much. You still need to return a result from the async function, which will return a promise to the caller. You can do that with something like:
async function getData() {
try {
let res = await axios({
url: 'https://jsonplaceholder.typicode.com/posts/1',
method: 'get',
timeout: 8000,
headers: {
'Content-Type': 'application/json',
}
})
if(res.status == 200){
// test for status you want, etc
console.log(res.status)
}
// Don't forget to return something
return res.data
}
catch (err) {
console.error(err);
}
}
getData()
.then(res => console.log(res))
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
But in this example, since you don't need to do much in the actual function with the result, you're probably better off just returning axios's promise:
function getDataPromise() {
return axios({
url: 'https://jsonplaceholder.typicode.com/posts/1',
method: 'get',
timeout: 8000,
headers: {
'Content-Type': 'application/json',
}
})
.then(res => res.data)
.catch (err => console.error(err))
}
getDataPromise()
.then(res => console.log(res))
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
It is not what you would like to hear but
Async/Wait works on the principle "Whatever Happens in Vegas - Stays in Vegas". It means you could not pass benefits of using blocking IO calls outside of async block.
If you want to use async/await to create some kind of blocking IO call it would not work unless a block caller is also inside an async function what is not normally the case.
async/wait is only good if you want to have a long chain of IO calls but entire chain still MUST be non-blocking. Individual calls inside chain might be blocking but full chain not.
Example
async fn(url) { //this is a non blocking function
let res = await axios.get("http://jsonservice1"); //blocking but only inside this function
let res2 = await axios.get(url+'?s='+res.data);//res.data is resolved already
return res2; //this how it returns results but it will not be resolved until .then is called what is effectively a callback
}
fn("google.com").then(R=>console.log('sorry I am not blocking '+R.data));
Coming from ajax, I prefer modular approach. Data to be sent, function on success and function ‎on fail are separate from function using axios. Bellow sample code fetch user email against ‎user name form node.js and mysql at the backend. ‎
HTML: <button onclick=" testaxios();">TestAxios</button>‎
JS in browser:
var data = {
username: "myusername"
}
async function testaxios() {
try {
let res = await axios({
method: 'POST',
data: data,
url: '/testmysql',
});
if (res.status == 200) {
success(res);
};
}
catch (error) {
fail(error);
};
}
function success(res) {
console.log(res.data[0][0].email);
}
function fail(error) {
console.log(error);
}
JS in nodeJS at backend:
app.post("/testmysql", async function (req, res) {
try {
let usrname = req.body.username;
let em = await pool.query("SELECT email FROM users WHERE username = ?", usrname);
res.send(em);
} catch (err) {
console.log(err);
} });

Categories