POST not finishing before .then() runs in async call - javascript

I'm making a POST request that takes ~10 seconds to finish... but my alert displays immediately on click, before the server returns 200. Why? And how do I fix it to wait until the server completes operation?
const handleClick = async () => {
const response = await fetch("/create", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(highlights),
}).then(alert("hi!"));
};
If I change my .then to
.then(() => {alert("hi!");}) as was suggested below, the page flashes/seems to refresh when the server request is made but the alert doesn't show at all
Update:
If I use this code:
const handleClick = async () => {
try {
const response = await fetch("/create", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(highlights),
});
alert("first");
} catch (error) {}
alert("second");
};
The second alert fires right when the button is pressed, the first alert never fires.

Async/await is introduced to overcome the then/catch chaining issue. You dont need to use then , you have to use try/catch. async/await always used with try/catch only.
const handleClick = async () => {
try{
const response = await fetch("/create", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(highlights),
})
alert("hi!")
}
catch(error){}
}
When you use try/ catch , control will not go further until i/o call is finished. So you can put alert or any code out of try catch block also. It will be executed once await call is completed.
Like this
try{
const response = await fetch("/create", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(highlights),
})
}
catch(error){}
}
alert("hi!")
Try this.
If you are using reactjs and nodejs, then I have simulated your requirement and it is working as expected.
I have used reactjs hooks.
Alert gets trigger once post call is succeeded.
You can check this out.
https://github.com/ajaysikdar/reactjs-nodejs-alert

Try:
.then(() => { alert("hi"); });
The then function expects you to pass it a function, but you are actually calling a function (alert), and passing the return value of that function to then.

Related

How to get parent component to re render after a fetch call in react?

I Want the parent component to re-render as this fetch request completes. With the current code it seems to be rendering before fetch completes no matter what. I'm guessing this is due to asynchronous nature of fetch? Is there a way this can be somehow achieved inside a .then() ? trying to call "this.props.rerenderParentCallback()" inside a .then() is giving me errors.
submit = (e) => {
e.preventDefault();
const myInit1 = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Vary': 'Origin'.replace,
'Accept': 'application/json'
},
body: JSON.stringify({
"username": this.state.username,
"password": this.state.password,
"email": this.state.email
})
};
console.log(myInit1.body);
let authurl = 'http://localhost:8080/authenticate';
if(!!localStorage.getItem("token"))
localStorage.removeItem("token");
try {
fetch(authurl, myInit1)
.then((response) => {
return response.json();
})
.then(function (myJson) {
console.log(myJson.token);
localStorage.setItem("token", myJson.token);
console.log("isAuth " + !!localStorage.getItem("token"));
}).then();
} catch (e) {
}finally{
this.props.rerenderParentCallback();
}
}
You're calling the function immediately, before the AJAX operation completes:
this.props.rerenderParentCallback();
AJAX is asynchronous. Anything you want to happen in response to an asynchronous operation would be in the .then() appended to the operation (or following an await, or in a passed-in callback, depending on how the asynchronous operation is structured).
In this case you'd also need to account for the fact that this means something different in that callback. For that, simply assign the function reference to a variable and reference that variable. Something like this:
const callback = this.props.rerenderParentCallback;
// then, in your .then() callback:
.then(function (myJson) {
console.log(myJson.token);
localStorage.setItem("token", myJson.token);
console.log("isAuth " + !!localStorage.getItem("token"));
callback(); // <--- invoke the function
})
(You can also get rid of the try/catch/finally structure entirely and just invoke the fetch operation. If the fetch might fail, append a .catch() to the .then() chain and handle it there.)
Why would the parent component need to re-render? You're not changing any of its state, thus not giving it a reason to re-render. Instead you can do something like this:
function MyComponent() {
const [someState, setSomeState] = React.useState(defaultValue);
console.log('Rendering with state', someState);
// whatever asynchronous operation you want
setTimeout(() => setSomeState(newValue), 1e3);
return <p>{new Date()}</p>;
}
You'll first see it render once with defaultValue, then after a second with newValue. With the useState hook, whenever you change the state from somewhere, it'll tell React "this component's state updated, please re-render it".
It's a bit rare in your case that you perform an operation and do something with the result that requires the component to re-render, even though the component doesn't seem to actually use the result? or at least not directly.
For your component, defaultValue could be read from localStorage. And whenever you call setSomeState, you can also directly save the new value to localStorage. Your component will be able to directly use the value without having to read localStorage every render.
You don't have access to this inside then. If you use arrow methods as below, you should be able to access it.
submit = (e) => {
e.preventDefault();
const myInit1 = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Vary': 'Origin'.replace,
'Accept': 'application/json'
},
body: JSON.stringify({
"username": this.state.username,
"password": this.state.password,
"email": this.state.email
})
};
console.log(myInit1.body);
let authurl = 'http://localhost:8080/authenticate';
if(!!localStorage.getItem("token"))
localStorage.removeItem("token");
try {
fetch(authurl, myInit1)
.then((response) => {
return response.json();
})
.then((myJson)=> {
console.log(myJson.token);
localStorage.setItem("token", myJson.token);
console.log("isAuth " + !!localStorage.getItem("token"));
}).then();
} catch (e) {
}finally{
this.props.rerenderParentCallback();
}
}
Here is another way of doing this using async await.
submit = async (e) => {
e.preventDefault();
const myInit1 = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Vary': 'Origin'.replace,
'Accept': 'application/json'
},
body: JSON.stringify({
"username": this.state.username,
"password": this.state.password,
"email": this.state.email
})
};
console.log(myInit1.body);
let authurl = 'http://localhost:8080/authenticate';
if(!!localStorage.getItem("token"))
localStorage.removeItem("token");
try {
const response = await fetch(authurl, myInit1);
const myJson = await response.json();
console.log(myJson.token);
localStorage.setItem("token", myJson.token);
console.log("isAuth " + !!localStorage.getItem("token"));
/*
* you may write it here
* this.props.rerenderParentCallback();
*/
} catch (e) {
}finally{
this.props.rerenderParentCallback();
}
}

synchronous fetch - unexpected results

First of all, I'm aware this is not a good approach, need it as temporary solution for certain functions to return value, not promise. I know it's really not good permanent solution at all, but I need it for now.
What worries me, fetch sure finishes sooner - but it runs until the whiles times out, and then to console comes first the RETVAL false, and only then second line comes RETFETCH: {....} with returned json values - it seems the 'haveResponse' value does not change in the second 'then' - but can't see why, and how to bypass it.
It's a temporary workaround for old sync fns to read some data from remote service running on local pc on some port, but for now I can't rewrite the function which expects to receive data from this fn, so there must be no promise on the outside, need to wait for response and then return it.
function syncFetch(url) {
var haveResponse = false;
var reqtime = new Date();
try{
fetch(url, {
headers: {'Content-Type': 'application/json'},
method: 'POST',
timeout: 1500,
body: JSON.stringify({cmd:'init'})
})
.then(response => response.json())
.then(data => {
console.log('RETFETCH:', data);
haveResponse = data;
return data;
});
// timeout
while (haveResponse === false) {
var endDate = new Date();
if (haveResponse !== false) { return haveResponse; }
if ((endDate - reqtime)/1000 > 5) { // max 5 sec
return haveResponse;
}
}
return haveResponse;
} catch(e){
console.log('error', e);
haveResponse = -1;
}
return haveResponse;
}
console.log('RETVAL',syncFetch('http://127.0.0.1:3333/'));
Save yourself a few headaches, drop all the .then() and use the async/await syntax instead. No need for dirty timeout/while hacks.
I renamed syncFetch to asyncFetch, because your original code was never synchronous in the first place (and that's precisely why you are struggling so much, you believe it is when it's not). async/await don't make the code synchronous either. It's just awesome syntactic sugar around Promises.
(EDIT : you said in the comments that you can't add the async keyword to the parent function (asyncFetch), so here's a workaround to place it inside :
function asyncFetch(url) {
async function a() {
try {
const response = fetch(url, {
headers: { 'Content-Type': 'application/json' },
method: 'POST',
timeout: 1500,
body: JSON.stringify({ cmd: 'init' })
});
const data = await response.json();
return data; // This is a Promise<data>, not data directly, so it needs to be awaited
} catch (e) {
console.log('error', e);
return null
}
};
return a();
};
(async () => {
console.log('RETVAL', await asyncFetch('http://127.0.0.1:3333/')); // Needs to be awaited, and therefore also needs to be inside an async function
})();

How to perform multiple axios POST requests in a For Loop with different body data for each iteration of loop?

I have an array of postData whose each element is the body that needs to be passed in every time we make an Axios POST request.
I am facing unhandled promise rejection error when running the script, i am fairly new to asynchronous programming and have been struggling with this error.
The script goes like this:
const axios = require('axios')
const fs = require('fs')
var textFileContainingData = fs.readFileSync('data.txt').toString().split("\r\n");
console.log(textFileContainingData);
let axiosConfig = {
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer XXXXXX',
'X-Correlation-ID': '1234',
}
};
for (i=0; i<textFileContainingData.length; i++) {
var postData = {
data: textFileContainingData[i]
};
console.log(postData)
const getData = async () => {
try {
return await axios.post('https://example-api-url/endpoint', postData, axiosConfig)
} catch (error) {
console.error(error)
}
}
const callEndpoint = async () => {
const responseData = await getData()
console.log(responseData)
}
callEndpoint();
}
My first answer was mistaken, I've removed it.
The error is related to a promise, probably axios.post is failing, post your stacktrace after trying your code
Some additional notes:
Use .forEach for readability
use axios.post({}).then(response => {}).catch(err => {}), you may omit .then and leave only .catch()

How to put a fetch request inside a function and wait for the return value when I call the function

I am using react-native, mongo DB and node js and I need to create some database functions and put them in some modules to be able to reuse them whenever I want. To fetch data from the mongo database, I use the fetch() function which returns a promise. So, for all functions that I created that did not return a value, I used .then and I faced no problems. On the other side, when I return a value inside a fetch().then() function and use this returned value, I get undefined. The code I use for the function looks like:
export const getUsers = () => {
//I cannot use this function because of returning a promise
fetch("http://1jjsd12zaws.ngrok.io/database/", {
method: "GET",
headers: {
"Content-Type": "application/json",
},
})
.then((res) => {
res.json();
})
.then((data) => {
return JSON.stringify(data);
});
};
Then, when I try to run this code:
let users=getUsers();
console.log(users);
It prints undefined.
What I think is going on is that the console.log(users) runs before getUsers() returns its value. But I do not know why does this happen and I want it to wait for getUsers() to execute then, completes its work.
You need to return fetch(..) inside getUsers (that's why you are getting undefined)
You also need to return res.json() inside the first then
Since getUsers returns a Promise, then you need to use .then (or async/await) to access the promise value: getUsers().then(users => {...})
const getUsers = () => {
return fetch('http://1jjsd12zaws.ngrok.io/database/', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
.then(res => {
return res.json();
})
.then(data => {
return JSON.stringify(data);
});
};
getUsers().then(users => console.log(users))
Async and await should cover it. The example on MDN docs explains it better than I can and should apply to your use case.

async fetch in function

I am trying to create a function, that sends a request to my API with fetch & waits for the result - if it returns data, I want the function to return true, otherwise false.
const loggedIn = async () => {
const response = await fetch(ENDPOINT_URL, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
query: query,
})
})
const json = await response.json()
// data gets logged
console.log(json.data)
if(json.data) {
return true
} else {
return false
}
}
If I call loggedIn(), I still receive PromiseĀ {<pending>}?
An async function returns a promise. From a caller's perspective, it's no different from any other function returning a promise. When you call it, you get a promise which you need to handle, not the value, because that would require blocking.
It sounds like you expected it to block your code. To get that, call it from another async function:
(async () => {
try {
console.log(await loggedIn()); // true or false
} catch (e) {
console.log(e);
}
})();

Categories

Resources