Multiple apis trigger without using settimeout - javascript

I have calling dataUpdate function every 4 second until records length to 0.
Inside the dataUpdate this.callAPI(); is trigger api call.
I set manual timeout for 4 seconds to call the function to callapi.
Instead of timeout ,how to call the api immediately after previous api completed.
dataUpdate =()=> {
var arrayList = this.arrayList;
if(arrayList.length > 0)
{
var inputData = {
...inputData,
data:{
...inputData.data,first:'',second:''
}
};
var first = arrayList[0].first;
var second = arrayList[0].second;
inputData.data.first = first;
inputData.data.second = second;
this.setState({ inputData:inputData });
this.callAPI();
arrayList.shift();
this.arrayList = arrayList;
if(arrayList.length !== 0){
setTimeout(() => {
this.dataUpdate();
}, 4000);
}
if(arrayList.length === 0){
setTimeout(() => {
this.props.callMessage(this.totalCount);
}, 1000);
}
}
}

Your problem is in having state. Make callapi function stateless and pass all needed parameters so you can call it any amount of times and do not rely on previous call or state

Related

JavaScript clear timeout not working with React useEffect return function

For some reason, I need to call checkProgess function for the first 2 minutes with 10 seconds of delays.
So I did it like this
const [timers, setTimers] = useState<ReturnType<typeof setTimeout>[]>([]);
useEffect(()=>{
for (let index = 0; index < 12; index++) {
const seconds = (index+1)*10000;
let timer = setTimeout(() => {
checkProgress(id, setFieldValue, contractType, fileOCRStatus);
}, seconds);
console.log("timer",timer)
setTimers((prev) => {
prev.push(timer)
return prev
});
}
},[])
within these 12 tries, this component will unmount if the progress checks succeed. In that time I do want to clear all the remaining timeout calls. I did it like this in the useEffect return function.
return () => {
console.log("Return function called",timers)
timers.forEach((timer) => clearTimeout(timer));
};
This part is executed successfully but the cleaning thing seems not working. I CAN SEE THE API CALLS RUNNING AFTER THE COMPONENT IS UNMOUNTED.
what went wrong here?
In console.log("Return function called", timers) timer ids also print correctly.
You don't actually need to store these in a state since this useEffect is only running once (and even if it wasn't, since you're cleaning up the timers in the returned function, you don't need to keep the value across renders).
// delete the useState stuff
useEffect(() => {
const timers = [];
for (let index = 0; index < 12; index++) {
const seconds = (index+1)*10000;
const timer = setTimeout(() => {
checkProgress(id, setFieldValue, contractType, fileOCRStatus);
}, seconds);
console.log("timer",timer)
timers.push(timer);
}
return () => {
timers.forEach((timer) => clearTimeout(timer));
}
}, []);

Call an API every 2 seconds for a period of one minute using react hooks

I am trying to make an api call (API1) every 2 seconds for a period of 1 minute. During this minute I need to exit the interval if condition is met and do another API call (API2). The problem that I am facing is if the API1 get into a pending state for more than 2 seconds, the second call of API1 will start and again if in pending state for 2 seconds the third call will happen ending up with quite few calls for same api all under pending state …. How can I stop the subsequent calls until the previous call is resolved?
useEffect(() => {
if (triggerTheInterval) {
startInterval()
return () => clearInterval(id)
}
}, [triggerTheInterval])
onClick = () => {
makefirstcallforapi1();
}
//this is only to get the first call at first second - before the 2 seconds interval starts
const makefirstcallforapi1 = () => {
setTimer(new Date().getTime());
fetch(url).then(res => {
if (conditionmet) {
//do the API2 call
} else {
setTriggerTheInterval(true)
}
})
startInterval = () => {
id = setInterval(() => {
fetch(url).then(res => {
if ((new Date().getTime() - startTime > 60000 || condtionmet) {
//do something then exit the interval
}
})
}, 2000)
}

Call a class method after another has finished its execution

I'm trying to execute a class method after another has finished. The first one calls an API and fills an array and a dictionary. Then, the next method takes the data and creates a new dictionary. I cannot make it work sequentially, any idea?
I have tried to add callback when calling the first method, but it ain't working.
this.fillListDictionary(function(){
this.fillCatDictionary();
});
fillListDictionary(callback) {
this._categoryList = [];
this._propertyDictionary = [];
//Fill list and dictionary calling the API ()
callback();
}
fillCatDictionary() {
this._catDictionary = [];
this._categoryList.forEach((category) => {
this._propertyDictionary.forEach((property) => {
if(property.properties.includes(category)){
var f_index = this._catDictionary.findIndex(x => x.category == category);
if(f_index >= 0){
this._catDictionary[f_index].dbIds.push(property.dbId);
}
else {
var cat = new Object();
cat.category = category;
cat.dbIds = [property.dbId];
this._catDictionary.push(cat);
}
}
})
})
}
I'd like to make it work sequentially: fillCatDictionary has to execute after fillListDictionary has done its job.

Promise 2 resolves before the Promise 1

I am doing a search in the textfield and as I type, there is a call going to the backend after say 100ms.
For example, if we search "5041" and immediately search for "50" and again make it "5041", then there are 3 calls made to the backend.
1."5041" -> Promise 1
2."50" -> Promise 2
3."5041" -> Promise 3
However, promise 3 (web call takes 200ms) resolves before promise 2 (web call takes 500ms) which makes the screen reflect results for promise 2 ("50") when all I have in the textfield is "5041".
I need some way to let user type in the textfield without blocking the user along with the ability to show results for only the last call.
This is something that can be achieved using switchMap from rxjs in an angular app. However I need a way to achieve the same in vanilla JS.
First you can wrap your fetchData function into a something like fetchLatestSearchResults function which notes the time when network call was made and return the latest result from all the network calls(irrespective of what data was returned from server)
const generateLatestSearchFetch = function(fetchFunc){
let mostRecentResult = null;
let mostRecentResultFetchTime = Date.now();
return (...args) => {
const myFetchStartTime = Date.now();
return fetchFunc(...args)
.then(data => {
if (myFetchStartTime > mostRecentResultFetchTime) {
mostRecentResult = data;
mostRecentResultFetchTime = myFetchStartTime
}
return mostRecentResult;
});
}
};
Use Like:
fetchData = generateLatestSearchFetch(fetchData);
fetchData('10'); // resolves first and returns result for 10
fetchData('102'); // resolves third and returns result for 1024
fetchData('1024'); // resolves second and returns result for 1024
Last but not the least, use debounce more on this to optimize number of network calls made for every type event.
You need a "last" function:
// takes a function returning a promise and only reports the last resort
function last(fn) {
let p;
return function(...args) {
let current = fn(); // call the function
p = current; // mark it as the last call
return p.then(result => {
// ask am I still the last call?
if (p === current) return result;
else return new Promise(() => {}); // never resolve
});
}
}
let onlyLastSearch = last((name) => fetch('/api?name=' + name));
onlyLastSearch('a'); // will be ignored
onlyLastSearch('b'); // will be ignored
onlyLastSearch('c'); // only relevant result
You can use observer pattern for this.
const createWrapper = (fn) => {
let counter = 0;
let lastFetchId = 0;
const listeners = [];
return {
fetch: (str) => {
let id = ++counter;
fn(str).then((data) => {
if(id > lastFetchId) {
listeners.forEach(fn => {
fn(data);
});
lastFetchId = id;
}
});
},
listen: (fn) => {
listeners.push(fn);
return () => {
const index = listeners.indexOf(fn);
listeners.splice(index, 1);
};
}
}
}
const SearchWrapper = createWrapper(fetchData);
SearchWrapper.fetch('a');
SearchWrapper.fetch('b');
SearchWrapper.fetch('c');
SearchWrapper.listen((data) => {
console.log(data);
})

Making multiple ajax requests synchronously

Let's suppose I have some function called makeRequest(), which makes an AJAX request to a server.
Now let's suppose I am given the amount of times this request should be made, but I can't do them asynchronously but synchronously instead.
For instance, I am given the number 5, and I shall call makeRequest(), when it's done, I shall call it again, and when it's done, I shall call it again... I should end up calling it 5 times.
I'm no expert at JavaScript but I found it easy to handle asynchronous calls by the use of callbacks.
So, my makeRequest() function takes a callback argument that is to be executed when the request has succeeded.
In my previous example, I had to make the request 5 times, so the behaviour should look like:
makeRequest(function () {
makeRequest(function () {
makeRequest(function () {
makeRequest(function () {
makeRequest(function () {
});
});
});
});
});
How can I design this to behave the same for any argument given to me, be it 6, 12 or even 1?
PS: I have tried many approaches, the most common involving creating while loops that wait until a flag is set by a finished request. All of these approaches makes the browser think the script crashed and prompt the user to stop the script.
Simple, recursively call the ajax request, while keeping track of a count variable:
function makeRequest(count, finalCallback){
someAjaxCall(data, function(){
if(count > 1){
makeRequest(count - 1, finalCallback);
} else {
finalCallback && finalCallback();
}
});
}
finalCallback is a optional callback (function) that will be executed when all the requests are completed.
You can do it this way,
var i = 5; // number of calls to you function calling ajax
recurs(i); // call it initially
function recurs(count) {
makeRequest(function() {
count--; // decrement count
if (count > 1) {
recurs(count) // call function agian
}
});
}
Here I have written multiple Ajax calls using promises. This function will run synchronously. You can get the current position of response which is executed from Ajax.
var ajaxs = {
i : 0,
callback : null,
param : null,
exec_fun : function (i) {
let data_send = this.param[i];
let url = this.url;
this.promise = new Promise(function (res,rej) {
$.ajax({
url: url,
method: 'POST',
data: data_send,
dataType: 'json',
success: function(resvalidate){
res(resvalidate);
}
});
});
this.promise.then(function (resvalidate) {
let resp = resvalidate,
param = ajaxs.param,
pos = ajaxs.i,
callback_fun = ajaxs.callback_fun;
callback_fun(resp,ajaxs.i);
ajaxs.i++;
if( ajaxs.param[ajaxs.i] != undefined){
ajaxs.exec_fun(ajaxs.i);
}
});
},
each : function (url,data,inc_callback) {
this.callback_fun = inc_callback;
this.param = data;
this.url = url;
this.exec_fun(ajaxs.i);
}
};
let url = "http://localhost/dev/test_ajax.php";
let data_param = [{data : 3},{data : 1},{data : 2}];
ajaxs.each(url,data_param, function (resp,i) {
console.log(resp,i);
});

Categories

Resources