Async function immediately call then() in recursive function - javascript

I have a problem, I tried to use async function to make API call but then() doesn't wait until the async function return the promise.
async function :
async function FgetFloorplansByFolder (idProject,idFolder, data = [], hasMore = false, lastSyncedAt = null) {
axios.get(API_URL, {
params:{
'last_synced_at':lastSyncedAt
},
headers: {
'Authorization': API_TOKEN,
'Accept': 'application/json'
}
})
.then((response) => {
let XHasMore = response.headers['x-has-more'];
let lastSyncedAt = response.headers['x-last-synced-at'];
for(var i in response.data) {
if(response.data[i].folder_id != null || response.data[i].folder_id == idFolder){
data.push(response.data[i])
}
}
if(XHasMore == 'true'){
FgetFloorplansByFolder(idProject,idFolder, data, XHasMore, lastSyncedAt)
}
else {
console.log(data);
return data
}
})
.catch((err) => {
return Promise.reject(err)
})
}
call of async function :
await FgetFloorplansByFolder(req.params.idProject, req.params.idFolder)
.then((result) => {
console.log(result);
})
.catch((error)=>{
console.log(error);
})
The expected result is : then function in the call wait until getFloorplansByFolders finish his recursive call and return data before print result in then. But then is printing undefined and doesn't wait until async function finish his call.
How can I do ?

Nothing in the code tells the function that it should wait for that promise to settle, so it doesn't.
In general, don't mix async/await with .then/.catch/.finally (though there are exceptions), use one or the other.
In this case, you can either
Remove the async and just put return in front of the call to axios to return the promise chain; or
Switch to using await within the function
(In both cases, I strongly urge you to remove the .catch handler that converts rejection into fulfillment with undefined; instead, let the caller see the rejection so they know the operation failed.)
#1 looks something like this (note comments):
// 1. No `async`
function FgetFloorplansByFolder (idProject,idFolder, data = [], hasMore = false, lastSyncedAt = null) {
// 2. Return the promise chain
return axios.get(API_URL, {
params:{
'last_synced_at':lastSyncedAt
},
headers: {
'Authorization': API_TOKEN,
'Accept': 'application/json'
}
})
.then((response) => {
let XHasMore = response.headers['x-has-more'];
let lastSyncedAt = response.headers['x-last-synced-at'];
for(var i in response.data) {
if(response.data[i].folder_id != null || response.data[i].folder_id == idFolder){
data.push(response.data[i])
}
}
if(XHasMore == 'true'){
// 3. Return the promise from the recursive call
return FgetFloorplansByFolder(idProject,idFolder, data, XHasMore, lastSyncedAt)
}
else {
console.log(data);
return data
}
});
// 4. Don't put a `.catch` here -- let the caller know the operation failed
}
#2 looks something like this:
async function FgetFloorplansByFolder (idProject,idFolder, data = [], hasMore = false, lastSyncedAt = null) {
const response = await axios.get(API_URL, {
params:{
'last_synced_at':lastSyncedAt
},
headers: {
'Authorization': API_TOKEN,
'Accept': 'application/json'
}
});
let XHasMore = response.headers['x-has-more'];
let lastSyncedAt = response.headers['x-last-synced-at'];
for(var i in response.data) {
if(response.data[i].folder_id != null || response.data[i].folder_id == idFolder){
data.push(response.data[i])
}
}
if(XHasMore == 'true'){
// 3. Return the result of the recursive call
return FgetFloorplansByFolder(idProject,idFolder, data, XHasMore, lastSyncedAt)
}
else {
console.log(data);
return data;
}
}

Related

How to return values from a nested Axios call?

I am trying to perform the below steps:
Step 1: Make Axios call to check if record exists in database.
Step 2: If the record does not exit then make a POST API call to create data and return POST response.
Step 3: If the record already exists then return the response from Step 1
Step 1 and Step 2 works fine and I am able to return the value from createProfileByUserIDNew. When code block for Step 3 gets executed then createProfileByUserIDNew is not returning any value.
Can someone tell me what am I doing wrong?
async createProfileByUserIDNew(data, email) {
const AuthStr = 'Bearer ' + getToken();
const response = await axios
.get(`${baseUrl}/buyer-profiles?user_email=${email}`, {
headers: { Authorization: AuthStr },
})
.then((response) => {
//*****This block return proper value in the next then
if (response.data.length === 0) {
return axios.post(`${baseUrl}/buyer-profiles`, data, {
headers: { Authorization: AuthStr },
});
//~~~~~~~This block return proper value in the next then
//*****This block does not return any value in the next then
} else {
return response //Return response from first axios call
}
//*****This block does not return any value in the next then
})
.then((response) => {
return (response); //Step 2 return the value but step 3 return undefined
})
.catch((error) => console.log(JSON.stringify(error)));
}
Calling the above method:
const ret = createProfileByUserIDNew(
data,
user.email
);
ret.then(function (response) {
console.log(response); //Setp 2 returns proper value but step 3 return undefined
this.setState({ buyerProfileId: response.items.id });
});
Remember that async/await is "syntactic sugar" for the other promise syntax of chaining .then(), .catch(), and .finally(); in other words, it allows you to handle these types of asynchronous operations in code that appears more synchronous.
const createProfileByUserIDNew = async (data, email) => {
const AuthStr = "Bearer " + getToken();
try {
// we're awaiting this response, so we don't need to chain a .then()
// we could even destructure response into the objects we'll need later,
// i.e. const { data } = await axios.get(...)
const response = await axios.get(
`${baseUrl}/buyer-profiles?user_email=${email}`,
{
headers: { Authorization: AuthStr },
}
);
if (response.data.length === 0) {
// do the things we need to do when we don't get the data we want
// once again, we don't have to chain a then() to this; you may
// have heard that 'return await' is redundant and causes some
// problems, but since we're in a try/catch it's ok
// see https://jakearchibald.com/2017/await-vs-return-vs-return-await/
return await axios.post(`${baseUrl}/buyer-profiles`, data, {
headers: { Authorization: AuthStr },
});
} else {
// the user exists, so we'll do other things, like maybe return the
// original response or something
return response;
}
} catch (error) {
console.error("We hit a snag:", error);
}
};
// now when we call this method (must be called from another async function), the same principles apply
const ret = await createProfileByUserIDNew(data, user.email);
console.log(ret);
This.setState({ buyerProfileId: ret.data.items.id });
That happens because when you simply do return response you're returning the response of the request, not a Promise. You should only chain the .then() when making the axios.post() call since that actually returns a Promise.
Also, if you want to use the createProfileByUserIDNew function the way you are currently you need to return the Promise from axios.get directly.
async createProfileByUserIDNew(data, email) {
const AuthStr = 'Bearer ' + getToken();
return axios.get(`${baseUrl}/buyer-profiles?user_email=${email}`, {
headers: { Authorization: AuthStr },
})
.then((response) => {
if (response.data.length === 0) {
return axios.post(`${baseUrl}/buyer-profiles`, data, {
headers: { Authorization: AuthStr },
})
.then((response) => {
return response;
});
} else {
return response;
}
})
.catch((error) => console.log(JSON.stringify(error)));
}

async function is not waiting for the end

My goal is, if the first request is catching an error, i dont want it to make another request. Basically conditional request.
Action.js
let condition;
Service.validation(payload).then(() => {
condition = true;
}).catch(errorMessage => {
console.log("1")
condition = false;
})
if (!condition) {
console.log("NOT valid.. closing..")
return;
} else {
console.log("VALID.. Calling another service")
const promise = AnotherService.anotherRequest(url, payload);
return promise.then().catch();
}
Service.js
async validation(payload) {
return this.createOrUpdate(payload, "check");
}
Generic class for crud
async createOrUpdate(data, subPath = "") {
try {
if (data.id) {
const response = await this.rc().put(this.PATH + subPath, data);
return response.data;
}
const response = await this.rc().post(this.PATH + subPath, data);
return response.data;
} catch (error) {
throw this.handleError(error);
}
}
rc = (responseType = "json") => {
return axios.create({
baseURL: this.RS_REST_URL,
validateStatus: (status) => (status === 200 || status === 201),
headers: {
"Content-Type": CONTENT_TYPE_JSON,
"Authorization": "Bearer " + localStorage.getItem(STORAGE_KEY_AT)
},
responseType: responseType,
paramsSerializer: (params) => qs.stringify(params, {arrayFormat: "repeat"})
});
}
I make a request const response = await this.rc().post gets an error (in Generic class) and thats i want. But, after i catch an error;
if (!condition) {
console.log("NOT valid.. closing..")
This part does not wait for service to set condition = false
Service.validation(payload).then(() => {
condition = true;
})**.catch(errorMessage => {
condition = false;
})**
it is an async function but code does not wait for it to finish.
IN THE CONSOLE
"NOT valid.. closing.."
"1"
Am i missing something here?
In Action. You need call await.
async function () => {
let condition;
await Service.validation(payload).....
if (!condition) ...
}

How to dynamic call the axios method in VueJS/NuxtJs

I'm trying to optimize my code. So, I dynamically use the axios function, but the returned response is a pending console log. I am using async/await. Any one can help me for this.
This is my code:
methods: {
getAgentsNames() {
const data = this.callAxios('get', `/getAllAgents`)
console.log(data) // returns pending
this.agents = data.listings
},
async callAxios(method, url, paramsBody = null) {
try {
const response = await this.$axios({
method,
url,
params: paramsBody,
headers: this.headers,
})
console.log(response) // success and have response data.
if (response.data.status == 'ok') {
return response.data
} else {
ifNotOK(response, this.$cookies)
return null
}
} catch (error) {
console.log(error)
return null
}
},
},
Your top function also needs to wait for the call to return:
async getAgentsNames() {
let data = await this.callAxios(

Return resolved promise value

const displayCharacters = async () => {
if(filteredCharacters !== 'default'){
const a = filteredCharacters.map(e => e.name);
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ 'data' : a })
};
const b = await fetch("/image",options).then(res => res.json())
return b;
}else{
return "yikes";
}
}
console.log(displayCharacters());
I have this fetch request but when I log the results this is what i see :
Promise {<resolved>: "yikes"}
__proto__: Promise
[[PromiseStatus]]: "resolved"
[[PromiseValue]]: "yikes"
I just want the promiseValue and not this whole thing around it. How do i do this?
the async function returns a promise instantly, without waiting for the promise to resolve. You may instead console.log inside the function:
const displayCharacters = async () => {
if(filteredCharacters !== 'default'){
const a = filteredCharacters.map(e => e.name);
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ 'data' : a })
};
try {
const b = await fetch("/image",options).then(res => res.json());
console.log(b);
//the better practice is however, to do like:
const b = await fetch("/image",options)
const result = await b.json();
console.log(result );
}
catch(err) {
console.log(err);
}
}else{
console.log("yikes");
}
}
displayCharacters();
The best way that i know to use fetch goes something like this:
const displayCharacters = async () => {
if(filteredCharacters !== 'default'){
const a = filteredCharacters.map(e => e.name);
const options = {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ 'data' : a })
};
const b = await fetch("/image",options)
.then(res => {
// Handle API Errors
if (!res.ok) {
throw Error(res.statusText);
}
// Return if no errors
return res.json();
})
// this is the data you want
.then(data => data)
// it will only reject on network failure or if anything prevented the request from completing
.catch(error => {
console.log(error.message)
});
return b;
}else{
return "yikes";
}
}
Basically you chain two thens and a catch to completely understand the response
- the first then checks for api level errors
- second then gets you the data
- catch is invoked in case when it is not able to reach the api itself like connection issues

Interference in data retrieval

I have three functions as shown:
async doAction (link, state)
{
try {
let response = await fetch(
'http://xxx.tld/'+link,
{
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
type:1
})
}, {timeout: 500});
let responseJson = await response.json();
alert(state)
switch (state) {
//somethings
.
.
.
default:
}
} catch (error) {
//alert(error);
}
}
async componentDidMount(){
this.doAction('loadModAdv.php', 1)
}
async loadModAdv(object){
object.map(async (data)=>{
if(data.status == "1")
{
await this.doAction(data.fileName, parseInt(data.moduleAdvertise, 10) + 1)
}
})
}
When "await this.doAction" is executed in the map, the "alert(state)" does not return the data in sequence.
Sometimes 1 2 4 and sometimes 1 4 2
What is the solution?
To achieve consistent/deterministic asynchronous iteration over an array, consider updating your code to use the following pattern:
async loadModAdv(object){
for(const data of object) {
if(data.status == "1") {
await this.doAction(data.fileName, parseInt(data.moduleAdvertise, 10) + 1)
}
}
}
As a note, I noticed you used the map function to iterate however it didn't seem that you're using the result of the map, so in this answer I've opted for a simpler solution based on a for loop.
If you wanted to achieve "map-like" behaviour via this pattern, you could do something like this:
async loadModAdv(object){
var results = []
for(const data of object) {
if(data.status == "1") {
results.push(await this.doAction(data.fileName, parseInt(data.moduleAdvertise, 10) + 1))
}
}
console.log('data in order ', JSON.stringify(results))
}
Hope that helps!

Categories

Resources