I'd like to consume data from a webservice and display it. The data has some dependencies on each other so that only one group of data can be displayed once all data of this group is read. All requests should start as fast as possible.
My code so far:
function asyncFetch(url = "", data = {}) {
return fetch(url, {
method: "POST",
mode: "cors",
cache: "no-cache",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
})
.then(response => (response.json()))
.catch(console.error);
}
const fetchOPDebitoren = () => {
console.log(" fetch fetchOPDebitoren");
return asyncFetch("http://server:3000/readOP", {
token: "mytoken",
options: {
vpkt: "1000",
bpkt: "6999"
}
});
};
const fetchOPKreditoren = () => {
console.log("fetch fetchOPKreditoren");
return asyncFetch("http://server:3000/readOP", {
token: "mytoken",
options: {
vpkt: "7000",
bpkt: "9999"
}
});
};
const fetchSalesData = () => {
console.log(" fetch SalesData");
return asyncFetch("http://server:3000/sales", {
token: "mytoken",
options: {
von_pkt: "10000",
bis_pkt: "99999"
}
});
};
const displayDueBarDashboard = () => {
Promise.all([fetchOPDeb(), fetchOPKred()])
.then(([op_data_deb, op_data_kred]) => {
console.log(" fetch OPs completed");
return new Promise((resolve) => {
/* display the data */
console.log("display op data");
});
})
.catch((error) => console.error('Error display DueBarChart:', error))
};
const displaySalesChart = () => {
Promise.all([fetchSalesData()])
.then(([sales_data]) => {
console.log("fetch SalesChart completed");
return new Promise((resolve) => {
/* display the data */
console.log("display sales data");
});
})
.catch((error) => console.error('Error display SalesChart:', error));
};
await Promise.all([
displayDueBarDashboard(),
displaySalesChart()
])
.then(() => {
console.log("All done, cleanup");
});
As you can see, to display the DueBars the data of both fetchOP
functions is needed.
With this code, the problem is now that the cleanup is called before all fetches are done and the display output is never shown.
fetch fetchOPDebitoren index.html:442:13
fetch fetchOPKreditoren index.html:465:13
fetch SalesData index.html:488:13
All done, cleanup index.html:563:13
fetch OPs completed index.html:539:14
fetch SalesChart completed
What am I doing wrong?
Also, in the fetch
functions I must present a token. This token is requested with another call of asyncFetch
which I have to wait for.
How can I implement that in the fetch functions without making them async?
The issue in your code is that your display functions don't properly resolve their promises, causing the main Promise.all()
to complete prematurely.
Both displayDueBarDashboard()
and displaySalesChart()
create promises that never resolve. When you return a new Promise without calling resolve()
, it stays pending forever, but the functions themselves still return immediately.
Additionally, you're referring to fetchOPDeb()
and fetchOPKred()
instead of your defined functions fetchOPDebitoren()
and fetchOPKreditoren()
.
const displayDueBarDashboard = () => {
return Promise.all([fetchOPDebitoren(), fetchOPKreditoren()])
.then(([op_data_deb, op_data_kred]) => {
console.log("fetch OPs completed");
/* display the data */
console.log("display op data");
// Return something or the promise will just pass through the results
return "DueBar completed";
})
.catch((error) => console.error('Error display DueBarChart:', error));
};
const displaySalesChart = () => {
return Promise.all([fetchSalesData()])
.then(([sales_data]) => {
console.log("fetch SalesChart completed");
/* display the data */
console.log("display sales data");
// Return something to resolve the promise
return "Sales completed";
})
.catch((error) => console.error('Error display SalesChart:', error));
};
To handle token acquisition first:
const getToken = () => {
return asyncFetch("http://server:3000/getToken", {
// token request parameters
}).then(response => response.token);
};
const fetchWithToken = (url, options) => {
return getToken().then(token => {
return asyncFetch(url, {
...options,
token: token
});
});
};
const fetchOPDebitoren = () => {
console.log("fetch fetchOPDebitoren");
return fetchWithToken("http://server:3000/readOP", {
options: {
vpkt: "1000",
bpkt: "6999"
}
});
};
Here's how you could restructure everything:
// Token acquisition
const getToken = () => {
return asyncFetch("http://server:3000/getToken", {})
.then(response => response.token);
};
// Cache token to avoid multiple requests
let tokenPromise = null;
const getTokenOnce = () => {
if (!tokenPromise) {
tokenPromise = getToken();
}
return tokenPromise;
};
// Data fetching functions
const fetchOPDebitoren = () => {
console.log("fetch fetchOPDebitoren");
return getTokenOnce().then(token => {
return asyncFetch("http://server:3000/readOP", {
token: token,
options: {
vpkt: "1000",
bpkt: "6999"
}
});
});
};
// Repeat same pattern for other fetch functions
// Display functions
const displayDueBarDashboard = () => {
return Promise.all([fetchOPDebitoren(), fetchOPKreditoren()])
.then(([op_data_deb, op_data_kred]) => {
console.log("fetch OPs completed");
console.log("display op data");
return "DueBar completed";
});
};
const displaySalesChart = () => {
return Promise.all([fetchSalesData()])
.then(([sales_data]) => {
console.log("fetch SalesChart completed");
console.log("display sales data");
return "Sales completed";
});
};
// Main execution
Promise.all([
displayDueBarDashboard(),
displaySalesChart()
])
.then((results) => {
console.log("All done, cleanup", results);
})
.catch(error => {
console.error("Error in main process:", error);
});
This approach ensures all promises resolve properly and handles token acquisition efficiently.