losing internet connection while sending multiple API calls one by one - javascript

I want to make multiple API calls one by one and the second one will be dependent on the result of the first one. What if I lost my internet connection in between each call?
Example:
const test1 = async () => {
const var1 = await function1()
// loss of internet connection or browser gets shut down
const var2 = await function2(var1)
}
Will var2 get executed or will it have expected result returned to me? If not, what are the workarounds to make sure two calls get executed?

If you try to call api without connection, you get an error. In your case error will be thrown when you call await someApiCall(). Workaround is to put some kind of retry logic.

Related

How can I use AbortController in Next js?

My application allows users to do searches and get suggestions as they type in the search box. For each time that the user enters a character, I use 'fetch' to fetch the suggestions from an API. The thing is that if the user does the search fast, he can get the result before the suggestions are fetched. In this case, I want to cancel the fetch request.
I used to have the same application in React and I could easily cancel the request using AbortController, but that isn't working in Next js.
I did some research and I think the problem is happening because Next doesn't have access to AbortController when it tries to generate the pages.
I also had this problem when I tried to use 'window.innerWidth' because it seems Next doesn't have access to 'window' either.
The solution I found was to use 'useEffect'. It worked perfectly when I used it with 'window'.
const [size, setSize] = useState(0)
useEffect(() => {
setSize(window.innerWidth)
}, [])
But it isn't working when I use AbortController. First I did it like this:
let suggestionsController;
useEffect(() => {
suggestionsController = new AbortController();
},[])
But when I tried to use 'suggestionsController', it would always be undefined.
So I tried to do the same thing using 'useRef'.
const suggestionsControllerRef = useRef(null)
useEffect(() => {
suggestionsControllerRef.current = new AbortController();
},[])
This is how I'm fetching the suggestions:
async function fetchSuggestions (input){
try {
const response = await fetch(`url/${input}`, {signal: suggestionsControllerRef.current.signal})
const result = await response.json()
setSuggestionsList(result)
} catch (e) {
console.log(e)
}
}
And this is how I'm aborting the request:
function handleSearch(word) {
suggestionsControllerRef.current.abort()
router.push(`/dictionary/${word}`)
setShowSuggestions(false)
}
Everything works perfectly for the first time. But if the user tries to do another search, 'fetchSuggestions' function stops working and I get this error in the console 'DOMException: Failed to execute 'fetch' on 'Window': The user aborted a request'.
Does anyone know what is the correct way to use AbortController in Next js?
The solution I found to the problem was create a new instance of AbortController each time that the user does the search. While the suggestions were being displayed, 'showSuggestions' was true, but when 'handleSearch' was called, 'showSuggestions' was set to false. So I just added it as a dependency to useEffect.
useEffect(() => {
const obj = new AbortController();
setSuggestionController(obj)
},[showSuggestions])
I also switched from useRef to useState, but I'm not sure if that was necessary because I didn't test this solution with useRef.
I don't know if that is the best way of using AbortController in Next js, but my application is working as expected now.
I suppose you can try an abort controller to cancel your requests if the user stops typing, but this is not the standard way of solving this common problem.
You want to "debounce" the callback that runs when the user types. Debouncing is a strategy that essentially captures the function calls and "waits" a certain amount of time before executing a function. For example, in this case you might want to debounce your search function so that it will only run ONCE, 500 ms after the user has stopped typing, rather than running on every single keypress.
Look into debouncing libraries or write a debounce function yourself, but fair warning it can be pretty tricky at first!

Handling errors and recoverying with node pg (postgres) client

I am using node module pg in my application and I want to make sure it can properly handle connection and query errors.
The first problem I have is I want to make sure it can properly recover when postgres is unavailable.
I found there is an error event so I can detect if there is a connection error.
import pg from 'pg'
let pgClient = null
async function postgresConnect() {
pgClient = new pg.Client(process.env.CONNECTION_STRING)
pgClient.connect()
pgClient.on('error', async (e) => {
console.log('Reconnecting')
await sleep(5000)
await postgresConnect()
})
}
I don't like using a global here, and I want to set the sleep delay to do an small exponential backoff. I noticed "Reconnecting" fires twice immediately, then waits five seconds and I am not sure why it fired the first time without any waiting.
I also have to make sure the queries execute. I have something like this I was trying out.
async function getTimestamp() {
try {
const res = await pgClient.query(
'select current_timestamp from current_timestamp;'
)
return res.rows[0].current_timestamp
} catch (error) {
console.log('Retrying Query')
await sleep(1000)
return getTimestamp()
}
}
This seems to work, but I haven't tested it enough to make sure it will guarantee the query is executed or keep trying. I should look for specific errors and only loop forever on certain errors and fail on others. I need to do more research to find what errors are thrown. I also need to do a backoff on the delay here too.
It all "seems" to work, I don't want to fail victim to the Dunning-Kruger effect. I need to ensure this process can handle all sorts of situations and recover.

How can i get the data from async call in order?

I am making http requests. When the request is done - with call back i am calling another function where the data from the async call is needed to make my logic.
On every call of my http request in lazyLoadData i am making new page requests for data - so page 1 with 25 records, page 2 with the next 25 records from backend etc...
getData() {
this.lazyLoadData(this.testing.bind(this));
this.lazyLoadData(this.testing.bind(this));
}
lazyLoadData(cb?) {
// MY HTTP REQUESTS
let response = axios...
cb && cb();
}
testing(data) {
// my data arraived
}
if i call just once
this.lazyLoadData(this.testing.bind(this));
then everytjing works fine, when the http request is done, my callback - the testing functions is called and i get the data.
But when i make call for example
two or more times
getData() {
this.lazyLoadData(this.testing.bind(this));
this.lazyLoadData(this.testing.bind(this));
this.lazyLoadData(this.testing.bind(this));
this.lazyLoadData(this.testing.bind(this));
}
sometimes call 2 - the second this.lazyLoadData(this.testing.bind(this)); is executed quicker then the first and i get my data mixed up. Because after i make the http request and i get, i am pushing that values in one global array with which my table is filled up.
And the data is not in order, so sometimes array contains first the data 25 records from
page 2, then page 1, page 3 etc...
How can i prevent this ?
Yes, you cannot call them sequentially like that because HTTP requests speed depends on many factors.
You can use the async/await function to solve your problem.
the async keyword always returns a promise, so you could combine it with the await keyword to tell Javascript to wait for your promise to be either resolved or rejected.
So for your code you could do something like this
async function lazyLoadData(){
//http requests
}
in the getData, you could simply :
function getData(){
// with this, the firstResponse variable will wait for the first call of lazyLoadData to be completed then assigned the value to itself
// before the execution move to the second call of the function
let firstResponse = await lazyLoadData(yourparam)
let secondResponse = await lazyLoadData(yourparam)
}
You might also want to read about Promise.all, as it could be an alternative to my solution.

Chaining requests in axios so I can do a second request for each element of first request

I need to get a list of friends from one endpoint and then get a number from a different endpoint for every friend I get from the first one. I found how to chain axios requests but only doing single requests one after another and I would like to do it in a loop, perfectly just adding it to every object in the list I get from first request. What I found and could be useful for that was using await so I have this:
async componentDidMount() {
const friendslist = await Promise.all(axios.get(`users/${global.userID}/friends`));
console.log(friendslist.data);
function myFun(item){
const data = await axios.get(`users/${global.userID}/sum/${item.id}`);
return data.sum;
}
friendslist.forEach(item.sum = myFun);
this.setState({loading: false, friends: friendslist.data})
};
but i don't even get my first request back, console just shows undefined. Sorry if the question was here already but I have not found anything similar or if my explanation is bad - it's my first question here. If it helps backend is in Django but I don't think it changes anything.

How to handle async code within a loop in JavaScript?

I am reaching out to a SongKick's REST API in my ReactJS Application, using Axios.
I do not know how many requests I am going to make, because in each request there is a limit of data that I can get, so in each request I need to ask for a different page.
In order to do so, I am using a for loop, like this:
while (toContinue)
{
axios.get(baseUrl.replace('{page}',page))
.then(result => {
...
if(result.data.resultsPage.results.artist === undefined)
toContinue = false;
});
page++;
}
Now, the problem that I'm facing is that I need to know when all my requests are finished. I know about Promise.All, but in order to use it, I need to know the exact URLs I'm going to get the data from, and in my case I do not know it, until I get empty results from the request, meaning that the last request is the last one.
Of course that axios.get call is asynchronous, so I am aware that my code is not optimal and should not be written like this, but I searched for this kind of problem and could not find a proper solution.
Is there any way to know when all the async code inside a function is finished?
Is there another way to get all the pages from a REST API without looping, because using my code, it will cause extra requests (because it is async) being called although I have reached empty results?
Actually your code will crash the engine as the while loop will run forever as it never stops, the asynchronous requests are started but they will never finish as the thread is blocked by the loop.
To resolve that use async / await to wait for the request before continuing the loop, so actually the loop is not infinite:
async function getResults() {
while(true) {
const result = await axios.get(baseUrl.replace('{page}', page));
// ...
if(!result.data.resultsPage.results.artist)
break;
}
}

Categories