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

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));

Related

Add both settimeout and timeout to in the array loop

I am looping the array using bluebird map method and then using each row as a payload to my apicall function. Everything works perfectly but I want to incorporate timeout method when response from the api takes more than 10 seconds and also settimeout method to delay 2 seconds after each api call. Please let me know how can I acheive this. I am fine without using bluebird. Thanks in advance.
handleSubmit = () => {
Promise.map(this.props.data, row => {
return apiCall(api, row).then((response) => {
if(response){
console.log(response)
} else{
console.log("failed")
}
})
}, { concurrency: 1 } )
}
apiCall: (api, input ) => {
switch (process.env.NODE_ENV) {
case 'production': { // Production environment
return new Promise((resolve) => {
window.runApi(api, input, (response) => {
if (typeof response === 'string') {
const jsonResponse = JSON.parse(response);
resolve(jsonResponse);
} else {
resolve(response);
}
});
});
}
default: {
const requestBody = input;
if (input !== "") {
requestBody.username = "user";
requestBody.password = "password";
}
const requestUrl = `api`;
return fetch(requestUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
})
.then(res => res.json());
}
}
}

Async function unexpected behavior

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;
}
);
});
}

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);
}

How to run a Node script

I need to be able to run a node script to delete an object from an external API. So I should be able to run this command:
node server.js Customer55555
And it should delete the object.
I have called to the API by using Axios.
const axios = require("axios");
const API = "http://dummy.restapiexample.com/api/v1/employees";
function getAllEmployees() {
axios
.get("http://dummy.restapiexample.com/api/v1/employees")
.then(response => {
// console.log(response.data);
console.log(response.status);
function filterEmployee() {
const employeeData = response.data;
employeeData.filter(employee => {
console.log(employee);
});
// console.log(employeeData);
}
filterEmployee();
})
.catch(error => {
console.log(error);
});
}
function deleteEmployee() {
axios({
method: "DELETE",
url: "http://dummy.restapiexample.com/api/v1/delete/36720",
headers: { "Content-Type": "application/json" }
})
.then(
// Observe the data keyword this time. Very important
// payload is the request body
// Do something
console.log("user deleted")
)
.catch(function(error) {
// handle error
console.log(error);
});
}
// getAllEmployees();
deleteEmployee();
I am able to get an individual object, but I need to figure out how to delete it by running the command above.
You can do something like this:
const axios = require("axios")
const API = "http://dummy.restapiexample.com/api/v1/employees"
async function getAllEmployees(filter = null) {
try {
const response = await axios.get("http://dummy.restapiexample.com/api/v1/employees")
console.log(response.status)
let employeeData = response.data
if (filter) {
// return only employees whose name contains filter.name
employeeData = employeeData.filter(({ employee_name }) => {
return employee_name.toLowerCase().indexOf(filter.name.toLowerCase()) >= 0
})
}
return employeeData
} catch(error) {
console.error(error)
return []
}
}
async function deleteEmployee({ id }) {
if (!id) {
throw new Error('You should pass a parameter')
}
try {
const response = await axios({
method: "DELETE",
url: `http://dummy.restapiexample.com/api/v1/delete/${id}`,
headers: { "Content-Type": "application/json" }
})
console.log("user deleted " + id)
} catch(error) {
// handle error
console.error(error)
}
}
async function main(params) {
const employees = await getAllEmployees({ name: params[0] || '' })
// Returns a promise to wait all delete promises
return Promise.all(employess.map(employee => deleteEmployee(employee)))
}
// process.argv contains console parameters. (https://stackoverflow.com/questions/4351521/how-do-i-pass-command-line-arguments-to-a-node-js-program)
main(process.argv.slice(2)).then(() => {
// returns 0 (Success) (https://stackoverflow.com/questions/5266152/how-to-exit-in-node-js)
process.exit(0)
}).catch(() => {
// returns 1 (error)
process.exit(1)
})
You should adapt this sample to get proper filtering and error reporting.

Use Async with .then promise

Hello after setup a simple async function with promise return i'd like to use then promise instead of try!
But is returning
await is a reserved word
for the second await in the function.
i've tried to place async return promise the data! but did not worked either
async infiniteNotification(page = 1) {
let page = this.state.page;
console.log("^^^^^", page);
let auth_token = await AsyncStorage.getItem(AUTH_TOKEN);
fetch(`/notifications?page=${page}`, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Access: auth_token
},
params: { page }
})
.then(data => data.json())
.then(data => {
var allData = this.state.notifications.concat(data.notifications);
this.setState({
notifications: allData,
page: this.state.page + 1,
});
let auth_token = await AsyncStorage.getItem(AUTH_TOKEN);
fetch("/notifications/mark_as_read", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Access: auth_token
},
body: JSON.stringify({
notification: {
read: true
}
})
}).then(response => {
this.props.changeNotifications();
});
})
.catch(err => {
console.log(err);
});
}
> await is a reserved word (100:25)
let auth_token = await AsyncStorage.getItem(AUTH_TOKEN);
^
fetch("/notifications/mark_as_read", {
You should refactor how you make your requests. I would have a common function to handle setting up the request and everything.
const makeRequest = async (url, options, auth_token) => {
try {
// Default options and request method
if (!options) options = {}
options.method = options.method || 'GET'
// always pass a body through, handle the payload here
if (options.body && (options.method === 'POST' || options.method === 'PUT')) {
options.body = JSON.stringify(options.body)
} else if (options.body) {
url = appendQueryString(url, options.body)
delete options.body
}
// setup headers
if (!options.headers) options.headers = {}
const headers = new Headers()
for(const key of Object.keys(options.headers)) {
headers.append(key, (options.headers as any)[key])
}
if (auth_token) {
headers.append('Access', auth_token)
}
headers.append('Accept', 'application/json')
headers.append('Content-Type', 'application/json')
options.headers = headers
const response = await fetch(url, options as any)
const json = await response.json()
if (!response.ok) {
throw json
}
return json
} catch (e) {
console.error(e)
throw e
}
}
appendQueryString is a little helper util to do the get qs params in the url
const appendQueryString = (urlPath, params) => {
const searchParams = new URLSearchParams()
for (const key of Object.keys(params)) {
searchParams.append(key, params[key])
}
return `${urlPath}?${searchParams.toString()}`
}
Now, to get to how you update your code, you'll notice things become less verbose and more extensive.
async infiniteNotification(page = 1) {
try {
let auth_token = await AsyncStorage.getItem(AUTH_TOKEN);
const data = await makeRequest(
`/notifications`,
{ body: { page } },
auth_token
)
var allData = this.state.notifications.concat(data.notifications);
this.setState({
notifications: allData,
page: this.state.page + 1,
});
const markedAsReadResponse = makeRequest(
"/notifications/mark_as_read",
{
method: "POST",
body: {
notification: { read: true }
},
auth_token
)
this.props.changeNotifications();
} catch (e) {
// TODO handle your errors
}
}

Categories