Async function unexpected behavior - javascript

At the moment I am working on an Electron app that is supplied with data via an API. The renderer calls a "backend function", which first gets the API key via Keytar and then executes the API call via axios.
The problem here is that Keytar always returns null/undefined, even if a similar function with the same functionality works without any problems, also because this point can only be reached if a valid API key is stored at all and this will also be queried by Keytar.
I am new to async/await-functions, maybe I didn't get something.
btw: Maybe the title doesn't fit too well, but I was a bit at a loss about this one.
(keytarService, username, baseUrl are globals)
Here is my code:
// Api-calling function
async function makeCall(method_type, url_path, data_array) {
keytar.getPassword(keytarService, username).then((apiKey) => {
if (apiKey == null || apiKey == undefined) {
return false;
}
axios({
method: method_type,
url: baseUrl + url_path,
headers: {
'content-type': 'application/json',
'X-AUTH-TOKEN': apiKey,
},
data: data_array,
}).then(
(response) => {
return response.data;
},
(error) => {
return false;
}
);
});
}
//index_renderer.js
webContents.on('dom-ready', () => {
apiMain
.makeCall('GET', 'user/self')
.then((data) => {
console.log(data);
document.getElementById('username_text').innerText =
data.firstName + '' + data.lastName;
})
.catch((err) => console.log(err));
});
Similar function which is working:
async function isAuthenticated() {
apiKey = await keytar.getPassword(keytarService, username);
if (apiKey == null || apiKey == undefined) {
return false;
}
axios({
method: 'GET',
url: baseUrl + '/api/isAuthenticated',
headers: {
'content-type': 'application/json',
'X-AUTH-TOKEN': apiKey,
},
data: {},
}).then(
(response) => {
console.log(response);
if (!response.data.authenticated) {
logout();
}
return response;
},
(error) => {
console.log(error);
logout();
return error;
}
);
}
// call of the working function in main.js
if (authProcess.isAuthenticated()) {
mainwin.loadFile('index.html');
} else {
mainwin.loadFile('login.html');
}
Thanks in advance.

You are missing important returns in MakeCall().
Try:
function makeCall(method_type, url_path, data_array) {
// return this promise to MakeCall
return keytar.getPassword(keytarService, username).then((apiKey) => {
if (apiKey == null || apiKey == undefined) {
return false;
}
// return this promise to keytar.getPassword then()
return axios({
method: method_type,
url: baseUrl + url_path,
headers: {
'content-type': 'application/json',
'X-AUTH-TOKEN': apiKey,
},
data: data_array,
}).then(
(response) => {
return response.data;
},
(error) => {
return false;
}
);
});
}

Related

Using fetch inside fetch is not executing all the fetch requests

I am trying to execute three fetch requests one by one. Each fetch request should trigger on completion of previous fetch request. Below is my code
const chopSegment = (token, frame_tag_url, tag_to_delete_id, chopped_tag_array, tags_for_index_update) => (dispatch) => {
let req = fetch(frame_tag_url + tag_to_delete_id + "/",
{
method: "DELETE",
headers: {
"Authorization": "Token " + token,
"content-type": "application/json"
}
})
req.then(response => {
if (!response.ok) {
throw response;
}
else
return response.json();
}).then(response => {
return fetch(frame_tag_url,
{
method: "POST",
headers: {
"Authorization": "Token " + token,
"content-type": "application/json",
},
body : JSON.stringify(tags_for_index_update)
}).then(response1 => {
if (!response1.ok) {
throw response1;
}
return response1.json();
}).then(response => {
for(let i = 0; i < chopped_tag_array.length; i++){
return fetch(frame_tag_url,
{
method: "POST",
body: JSON.stringify(chopped_tag_array[i]),
headers: {
"Authorization": "Token " + token,
"content-type": "application/json"
}
})
.then(response2 => {
if (!response2.ok) {
throw response2;
}
return response2.json();
}).then(response2 => {
dispatch(chopSegmentSuccess(response2))
}).catch(error => {
})
}
}).catch(error => {
})
}).catch(error => {
})
}
In my code, only first fetch i.e. "DELETE" gets executed? What am I doing wrong?
You can't do fetches in a loop. You're returning the first fetch that completes. Use promises or await/async to fetch in a loop.
How to return many Promises in a loop and wait for them all to do other stuff
I'd rather do it this way, Create an IIFE and call it recursively for the subsequent fetch request:
return dispatch =>{
var ctr = 0;
(function myFunc(url, headerObj){
fetch(url, headerObj)
.then(response => {
response.json().then(data=>{
ctr++;
if(ctr ===1 ){ // This could be any condition, say, something on the basis of response; I have taken `ctr` as a condition
myFunc(url, { //You may change this even to different URL, if needed
method: 'POST',
headers: {
'content-type': 'application/json',
'body': ...,
'Authorization':...
}
});
}else if(ctr === 2){
myFunc(url, {
method: 'POST',
headers: {
'content-type': 'application/json',
'body': ...,
'Authorization':...
}
});
}else{
// Any other code
}
})
})
})(url, headerObj);
}

Paypal Expected an order id to be passed

I have integrated Paypal smart button to my page and it works. 3 days ago Do not pass Pay-xxx directly gave an error and told me to send a token instead. This time, when I make a reference to it, it gives an error: Expected an order id to be passed. What should I do?
The following code raises an error: Expected an order id to be passed
var CREATE_PAYMENT_URL = '/api/create-payment';
var checkBox = document.getElementById("ship_to_different");
var note = $("#ordernote").val();
if (checkBox.checked == true) {
var body = $("#checkoutt, #data").serializeArray();
} else {
$('input[name=note]').val(note);
var body = $("#data").serializeArray();
}
$("#wait").show();
return fetch(CREATE_PAYMENT_URL, {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
body: body
})
}).then(function (res) {
return res.json();
}).then(function (data) {
console.log(data);
let token;
for (let link of data.links) {
if (link.rel === 'approval_url') {
token = link.href.match(/EC-\w+/)[0];
}
}
return data.token;
});
The following code raises an error:
Do not pass PAY-XXX or PAYID-XXX directly into createOrder. Pass the EC-XXX token instead
var CREATE_PAYMENT_URL = '/api/create-payment';
var checkBox = document.getElementById("ship_to_different");
var note = $("#ordernote").val();
if (checkBox.checked == true) {
var body = $("#checkoutt, #data").serializeArray();
} else {
$('input[name=note]').val(note);
var body = $("#data").serializeArray();
}
$("#wait").show();
return fetch(CREATE_PAYMENT_URL, {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
body: body
})
}).then(function (res) {
return res.json();
}).then(function (data) {
console.log(data);
let token;
for (let link of data.links) {
if (link.rel === 'approval_url') {
token = link.href.match(/EC-\w+/)[0];
}
}
return data.id;
});
I am not able to understand what is going on.
If you are using .net, then in your create payment api call, return an object that contains the order id instead of returning the BraintreeHttp.HttpResponse
[HttpPost]
public async Task<Order> Post()
{
try
{
var request = new OrdersCreateRequest();
request.Prefer("return=representation");
request.RequestBody(BuildRequestBody());
//3. Call PayPal to set up a transaction
var response = await PayPalClient.client().Execute(request);
Order order = new Order();
var result = response.Result<Order>();
if (result?.Status?.Trim()?.ToLower() == "created")
{
order.OrderId = result.Id;
}
return order;
}
catch (Exception ex)
{
string m = ex.Message;
throw;
}
}
then on my js
paypal.Buttons({
createOrder: function () {
return fetch('/api/paypal', {
method: 'post',
headers: {
'content-type': 'application/json'
}
}).then(function(res) {
return res.json();
}).then(function (data) {
return data.orderId; // the data is the order object returned from the api call, its not the BrainTree.Response object
});
}
Sorry for sloppy code, I was still in the process of trying to figure out the problem. Hope you get the gist of the solution
I also faced this issue, and I resolved easily.
paypal.Buttons({
createOrder: function () {
return fetch('/api/create_payment/', {
method: "POST",
headers: {
'content-type': 'application/json',
'X-CSRFToken': $('[name=csrfmiddlewaretoken]').val()
}
}
)
.then(function(res) {
return res.json();
}).then(function(res) {
return res['orderId'];
});
},
onApprove: function (data, actions) {
// Do something after approved...
}
}).render('#paypal-container');
Here important is, on the back-end, I created order with Paypal V2 api endpoint - https://developer.paypal.com/docs/api/orders/v2/#orders_create
You need to send the Token of the payment and not the ID of the payment.
If you use php, use this :
// Instance of your ApiPayment
$payment = new ApiPayment();
$payment->getToken();
Your first code should work. My subjection is to check your browser console whether there are any errors or not.
Ref:
https://developer.paypal.com/docs/checkout/reference/upgrade-integration/?mark=Ec%20token#4-set-up-the-transaction
Code:
createOrder: function(data, actions) {
return fetch('{{ route('create-payment') }}', {
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
method: 'post',
body: JSON.stringify({
body:body
})
}).then(function(res) {
return res.json();
}).then(function(orderData) {
console.log(orderData);
// If you don't use ES2015 or higher replace 'let' with 'var'
var token;
for (link of orderData.links) {
if (link.rel === 'approval_url') {
token = link.href.match(/EC-\w+/)[0];
}
}
return token;
});
},
If you are using paypal sdk then make sure you are returning the order
createOrder={(data, actions) => {
return this.createOrder(cartState, actions)
}}
In your first JS code example, try returning the token variable instead of data.token. Data.token in that example is just going to return an object (which when I ran your code, it return the entire payment object). If you return simply the token variable, it will return the required orderID value (EC-XXXXX). I've adjusted your code example below.
var CREATE_PAYMENT_URL = '/api/create-payment';
var checkBox = document.getElementById("ship_to_different");
var note = $("#ordernote").val();
if (checkBox.checked == true){
var body = $("#checkoutt, #data").serializeArray();
}else{
$('input[name=note]').val(note);
var body = $("#data").serializeArray();
}
$("#wait").show();
return fetch(CREATE_PAYMENT_URL, {
method: 'post',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
body:body
})
}).then(function (res) {
return res.json();
}).then(function (data) {
console.log(data);
let token;
for (let link of data.links) {
if (link.rel === 'approval_url') {
token = link.href.match(/EC-\w+/)[0];
}
}
return token;
});
},
I was getting this because of a return false inside of the createorder function. Cart value input was not there, missed uploading a file.
Might be helpful :)
createOrder: function (data, actions) {
var cartVal = $("body").find("#cartVal").val();
if (typeof cartVal === 'undefined')
return false;
});

function based on other callBack function... react-native

when user wants to to POST somthing he must be singed in(without username & pass).
Problem is i'm trying to make when CreatePost() invoked it will call SingUser() and based on SingUser() fetch request it will call CreatePost() again to let user post after he sign in.
this is in createpost component
CreatePost(){
fetch(url ,{
method :'POST',
headers:{
Accept:'application/json',
'Content-Type' :'application/json',
},
body: JSON.stringify(post)
}).then((response) => response.json())
.then((responseJson)=>{
if(responseJson.status =='inactive'){
//SignUser
}else{
//post
}
}).catch((error)=>{ //later
});
}
here is SingUser() in other file
async function SignUser() {
try{
User.vtoken = await AsyncStorage.getItem('vtoken');
var userTemp={
vtoken: User.vtoken,
ntoken : User.ntoken
}
fetch(url,{
method :'POST',
headers:{
Accep : 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(userTemp)
}).then((response)=> response.json()).
then((responseJson)=>{
if(responseJson.path == 2){
Save(responseJson, userTemp);}
else return;
}).catch((error)=>{
});
}catch(error){}
}
async function Save(result , userTemp){
try{
await AsyncStorage.setItem('vtoken', result.vtoken);
User.vtoken = result.vtoken;
userTemp.vtoken = result.vtoken;
fetch(url,{
method :'POST',
headers:{
Accep : 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(userTemp)
}).then((response)=>response.json()).
then((responseJson)=>{
return 'done';
}).catch((error)=>{})
}
catch(error){}
}
export {SignUser}
i hope u understand what im trying to do if there is better way to do it thnx:(
You can do something like this:
const errorCodeMap = {
USER_INACTIVE: 10,
}
const statusMap = {
INACTIVE: `inactive`
}
const METHOD = `POST`
const APPLICATION_JSON = `application/json`
const headerDefault = {
Accept: APPLICATION_JSON,
'Content-Type': APPLICATION_JSON,
}
const who = `post`
async function createPost(payload, options) {
try {
const {
url = ``,
fetchOptions = {
method: METHOD,
headers: headerDefault,
},
} = options
const {
post,
} = payload
const response = await fetch(url, {
...fetchOptions,
body: JSON.stringify(post)
})
const {
status,
someUsefulData,
} = await response.json()
if (status === statusMap.INACTIVE) {
return {
data: null,
errors: [{
type: who,
code: errorCodeMap.USER_INACTIVE,
message: `User inactive`
}]
}
} else {
const data = someNormalizeFunction(someUsefulData)
return {
data,
errors: [],
}
}
} catch (err) {
}
}
async function createPostRepeatOnInactive(payload, options) {
try {
const {
repeat = 1,
} = options
let index = repeat
while (index--) {
const { data, errors } = createPost(payload, options)
if (errors.length) {
await signUser()
} else {
return {
data,
errors,
}
}
}
} catch (err) {
}
}
solve it, I did little adjustments
async CreatePost(){
try{
var response = await fetch(url ,{
method :'POST',
headers:{
Accept:'application/json',
'Content-Type' :'application/json',
},
body: JSON.stringify(post)});
var responseJson = await response.json();
if(responseJson.status =='inactive' && postRepeat == true){
postRepeat == false;
await SignUser();
this.CreatePost();
}
else{
//posted
}
}catch(err){}
}

How to access the request object in a fetch then/response function

I have a JavaScript loop iterating over an array. For every item, I perform a fetch request to insert the object. If the server response indicates it is an already inserted object, I try an update operation with another fetch call.
As the requests are asynchronous, the loop sets the request object to the next insert item before I try the update operation, so I end up requesting an update for an object it's not yet inserted.
Is there any way I can access the request object used for this fetch operation, so I can use that object instead of the loop var?
I've tried with this within the promise method, but it returns a reference to the window object: console.log(this) ==> > Window http://localhost
My code:
for (var i = 0; i < expectedRows; i++) {
var row = myArray[i];
customerCode = row['customer_code'];
customerName = row['customer_name'];
customerBalance = row['customer_balance'];
// Build body call
var callBody = {
user: 'USER',
code: customerCode,
name: customerName,
balance: customerBalance
};
var fetchOptions = {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
redirect: "error",
referrer: "ux-import",
body: JSON.stringify(callBody),
};
// Call
var epurl = baseEP + '/customer/create';
fetch(epurl, fetchOptions)
.then(response => response.json())
.then(response => {
console.log(this) // <== Window object reference
if (response.error === 0) {
console.log('insert ok');
insertRows++;
} else {
if (response.error == 2) {
console.log('insert error => update');
var updateEP = baseEP + '/customer/update';
fetch(updateEP, fetchOptions) // <== Not what you expect
.then(updResponse => updResponse.json())
.then(updResponse => {
if (updResponse.error === 0) {
console.log('update ok.')
updateRows++;
} else {
console.log('update error: ' + updResponse.msg)
errorMessages.push(updResponse.msg);
}
})
.catch(error => {
console.log('update failure');
errorMessages.push(error);
});
} else {
console.log('insert error.');
errorMessages.push(response.msg);
}
}
})
.catch(error => {
console.log('insert failure.');
errorMessages.push(error);
});
}
I need some way to access this fetch call request object to achieve something like this:
var updFetchOptions = {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
redirect: "error",
referrer: "ux-import",
body: this.request.body, // this as a reference to this fetch's request
}
fetch(updateEP, updFetchOptions)...
:
:
Can you try this.
for (let i = 0; i < expectedRows; i++) {
let row = myArray[i];
customerCode = row['customer_code'];
customerName = row['customer_name'];
customerBalance = row['customer_balance'];
// Build body call
let callBody = {
user: 'USER',
code: customerCode,
name: customerName,
balance: customerBalance
};
let fetchOptions = {
method: "POST",
cache: "no-cache",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
redirect: "error",
referrer: "ux-import",
body: JSON.stringify(callBody),
};
// Call
let epurl = baseEP + '/customer/create';
fetch(epurl, fetchOptions)
.then(response => response.json())
.then(response => {
console.log(this) // <== Window object reference
if (response.error === 0) {
console.log('insert ok');
insertRows++;
} else {
if (response.error == 2) {
console.log('insert error => update');
let updateEP = baseEP + '/customer/update';
fetch(updateEP, fetchOptions) // <== Not what you expect
.then(updResponse => updResponse.json())
.then(updResponse => {
if (updResponse.error === 0) {
console.log('update ok.')
updateRows++;
} else {
console.log('update error: ' + updResponse.msg)
errorMessages.push(updResponse.msg);
}
})
.catch(error => {
console.log('update failure');
errorMessages.push(error);
});
} else {
console.log('insert error.');
errorMessages.push(response.msg);
}
}
})
.catch(error => {
console.log('insert failure.');
errorMessages.push(error);
});
}
Basically, defining variables with var is not a good method as it doesn't maintain its state with each iteration of loop. But using let maintains the variable state for each iteration and you can use the variable even after doing some async task like fetch in your case.
You can achieve this by explicitly creating the RequestInit-Object and wrapping your handler functions like this:
const initObject = {
method: 'POST',
something: 1234
};
fetch('/test.json', initObject)
.then(r => r.json())
.then(((initObject) => {
return json => {
console.log({json, initObject})
}
})(initObject));

How to use async await in React using the axios in the API call

I am new to the web development. I am using react.js.So, Here I want to use the async/awaitfor the API call. I am using axios.
Now,
what I have is like
export function fetchToken(bodyjson) {
return (dispatch) => {
let url = LOGIN_PATH + "username=" + bodyjson.userName + "&password" + "=" + bodyjson.password;
return post(url, bodyjson)
.then((response) => {
if (response.status === 200) {
localStorage.setItem('user', bodyjson.userName);
localStorage.setItem('access_token', response.payload.access_token);
history.push('/');
dispatch({
type: LOGIN_SUCCESS,
data: response.payload,
})
}
else {
dispatch({
type: LOGIN_FAILED,
data: response.status,
});
}
})
}
}
and my post service is like,
export const post = (url, post_data) =>
axios.post(
apiGatewayEndpoint.apiGatewayEndpoint + url,
post_data,
{
headers: {
"Authorization": localStorage.getItem("access_token") !== null ? `Bearer ` + localStorage.getItem("access_token") : null,
"Content-Type": "application/json"
}
}
).then(data => {
if (data.status === HttpStatus.OK) {
return {
status: data.status,
payload: data.data
};
}
}).catch(err => {
return {
status: err.response.data,
payload: null
};
});
Now, I want to use the async await over here. I am very confused between this. I have gone through lots of the tutorials.
I want to call an API immediately after the login. on that basis I want to redirect user to the diff pages.
So, can any one help me with this async-await
THANKS:-)
Now I am using it like,
export function fetchToken(bodyjson) {
return async (dispatch) => {
let url = LOGIN_PATH + "username=" + bodyjson.userName + "&password" + "=" + bodyjson.password;
let response = await post(url, bodyjson)
if (response.status === 200) {
localStorage.setItem('user', bodyjson.userName);
localStorage.setItem('access_token', response.payload.access_token);
let fetchJd = FETCH_JD_ROOT_URL + page + "&" + size;
let newApiResponse = await get(fetchJd)
if (newApiResponse.status === 200) {
dispatch({
type: LOGIN_SUCCESS,
data: response.payload,
})
dispatch(sendUserJd(newApiResponse.payload));
}else {
dispatch({
type: LOGIN_FAILED,
data: response.status,
});
}
}
else {
dispatch({
type: LOGIN_FAILED,
data: response.status,
});
}
}
for get requests, you can use params to send data etc etc.
export const getData = async () => {
try {
const { data } = await axios({
method: 'get', //you can set what request you want to be
url: `yoururl`,
params: {
// key values pairs
}
headers: {
'token': token
}
});
// run some validation before returning
return data;
} catch (e) {
console.log(e);
return .. some error object;
}
};
for post request
export const getData = async (params) => {
try {
const { data } = await axios({
method: 'post', //you can set what request you want to be
url: `url`,
data: params,
headers: {
'x-auth-Token': token
}
});
// run some validation before returning
return data;
} catch (e) {
console.log(e);
return .. some error object;
}
};
error object example
{
status: 'error',
message: 'failed with something'
}
then you can call any api like this,
async componentDidMount() {
const data = await getData();
if(data.status === 'Something') {
// do something
}
}
You dont exactly need async await for this purpose.
With then chain approach
export function fetchToken(bodyjson) {
return (dispatch) => {
let url = LOGIN_PATH + "username=" + bodyjson.userName + "&password" + "=" + bodyjson.password;
return post(url, bodyjson)
.then((response) => {
if (response.status === 200) {
localStorage.setItem('user', bodyjson.userName);
localStorage.setItem('access_token', response.payload.access_token);
history.push('/');
dispatch({
type: LOGIN_SUCCESS,
data: response.payload,
})
//next api call
return post(newUrl, newBodyjson)
}
else {
dispatch({
type: LOGIN_FAILED,
data: response.status,
});
}
})
.then((newApiResponse) => {
//Do stuffs with new api response
})
}
}
But if you want to use async-await approach only
export function fetchToken(bodyjson) {
return async (dispatch) => {
let url = LOGIN_PATH + "username=" + bodyjson.userName + "&password" + "=" + bodyjson.password;
let response = await post(url, bodyjson)
if (response.status === 200) {
localStorage.setItem('user', bodyjson.userName);
localStorage.setItem('access_token', response.payload.access_token);
history.push('/');
dispatch({
type: LOGIN_SUCCESS,
data: response.payload,
})
let newApiResponse = await post(newUrl, newBodyjson)
//Do stuffs with new api response
}
else {
dispatch({
type: LOGIN_FAILED,
data: response.status,
});
}
}
}

Categories