in my Javascript application the user can press a button, in order to continuously fetch data from a server.
Pressing the button triggers a function, which performs a HTTP request to the server. After the request is completed successfully, this function calls itself recursively again and again. The function is escaped if the user presses the button again or a specific limit is reached.
Now my problem is, that by each recursion the delay between each request gets bigger. This can be visualized in the screenshot I took from the developer tools of my browser:
The blue lines are my sets of requests in each function. The white empty slots between the sets of requests get longer.
This is how my code is structured:
async function startContinousFetch() {
addContinousData(true);
}
async function addContinousData() {
addNewData().then((resp) => {
if (LOGSLIMIT != null && Logs.length > LOGSLIMIT) {
stopContinousFetch();
return;
}
//Unexpected error, promise didn't resolve with true;
if (resp == false) {
//Error handling
}
if (continousFetch) {
addContinousData();
}
})
}
function addNewData() {
return new Promise(async resolve => {
try {
//Various http requests are here
await fetchData();
//Add, organize and manipulate data in the global "Logs" array
handleLogs();
//Resolve promise
resolve(true)
} catch (e) {
resolve(false, e)
}
})
}
Why do the delays get longer? What is my mistake here?
It is clear to me that my function waits till the response, so there is a delay. But why does this delay get bigger every time.
In the dev tools, the request time does not get any longer, just the time in between.
Thanks a lot.
Related
I'm currently doing some post requests within an interval function:
let getStatusIV = setInterval( () => {
let data = {
action: 'get_products_status',
import_id: importId
};
$.post( ajaxurl, data, function () {
} ).done( response => {
if (response.success && response.data) {
// showLoadingSpinner( '#wpcontent', response.data ); <- Sets the text at an overlay
}
} )
}, 500 );
This interval / post function returns the status of another AJAX call from my backend - for example: Processed 20 of 700 products
The problem is, that the requests are not resolving in the order they were called so as a result I'm getting this example:
Processed 20 of 700 products
Processed 19 of 700 products
Processed 30 of 700 products
Processed 80 of 700 products
Processed 75 of 700 products
Any idea to fix this? I really need short intervals so just settings a longer interval time dont helps.
That's how asynchronous programming works. Since you have so much things to be processed, keep calling .then() won't be that practical then. But why is the order so important? In reality most operations are processed asynchronously to maximize efficiency. I'm a newbee by the way, but I did encounter this, I just made everything asynchronous to solve the same problem.
------------------------------------
Okay, now I find a way to accomplish this. This is an example from JavaScript: The Definitive Guide by David Flanagan. You may want to check it out because it's indeed AWESOME.
function fetchSequentially(urls) {
const bodies = [];
//Response bodies are stored here
function fetchOne(url) {
return fetch(url)
.then((response) => response.text())
.then((body) => {
bodies.push(body);
});
}
let p = Promise.resolve(undefined);
//Fulfill an empty promise to initiate the promise chain
for (url of urls) {
p = p.then(() => fetchOne(url));
//Build the promise chain automatically
}
return p.then(() => bodies);
/*
Return bodies. It's a promise so you may call
.then() one last time to get the output.
For the progress bar, you may want to use event
to track how many requests have been done.
*/
}
Actually you can just use event to deal with the progress bar problem to keep the requests "parallel". Just emit an event to announce that one request has done. Listen to the event and track how many instead of which one has finished. Then the progress bar will never go backwards.
I have a function that do some HTTP requests, i need to do them often every certain amount of time (in this case 5 seconds) if a certain condition is triggered.
What happens is that sometimes the requests that are inside the setInterval loop takes more than the specified time and the loop triggers again, calling the request again, without waiting for the previous to resolve.
function doRequests() {
setInterval(async () => {
//Sometimes the following line takes more than 5 seconds to return its promise
const response = await myRequestFunction();
// do something with response...
if(/*certain condition is triggered*/)
{
//Call the function again
doRequests();
}
}, 5000);
}
doRequests();
I've already tried doing a recursive setTimeOut function like in this post, it worked but a few requests later it simply stopped working, not because a stack overflow happened but because IT SIMPLY STOPPED! The request was done, but it never brought the response back so the code stopped. And a few time before it stops it got slow.
I've also already tried using a setTimeOut inside a while loop but it seems that loops doesn't wait for intervals, not within this async context.
So the solution i need is: A way to every 5 seconds do the requests, but if it takes more than that to return the response, await for it.
Well, here is a solution in order to keep the cycle period as most regular as possible. As long the responses arrive within the 5s timeout, the cycle polling should be pretty regular. However, if a response comes in late, the new request is made immediately it is processed.
I also added a try-catch block, because it is likely a chance having some error on a HTTP request (and also because you're doing other stuffs in the while). This leads you to decide how to behave in such a cases.
async function doRequests() {
let answered = false;
let expired = false;
//start the timer
let tmr = setTimeout(function() {
if (answered) {
//response already received (and processed), so start a new request
doRequest();
}
else {
//mark the timer as expired
expired = true;
},
5000
);
try {
const response = await myRequestFunction();
// do something with response...
if(/*certain condition is triggered*/)
{
answered = true;
if (expired) {
//Call the function again
doRequests();
}
}
}
catch (err) {
//error trapped: clear timeout
clearTimeout(tmr);
if (/* decide to continue as it was fine */) {
answered = true;
if (expired) {
//Call the function again
doRequests();
}
}
}
}
doRequests();
I create a simple queue-job system with using BullQueue in TypeScript and NestJS like below:
async addToQueue(): Promise<void> {
try {
await this.reportQueue.add('generate_report', {
//some data
})
this.logger.log(`Added to queue.`)
} catch (error) {
this.logger.error(`Not added to Queue.`);
}
}
#Process('generate_report')
async generateReport(job: Job<{options: ReportFilterDto, context: CustomContext, reportType: Type, worktime?: WorktimeDto}>): Promise<any> {
try {
//some working code
} catch (error) {
//
}
}
#OnQueueActive()
async onActive(job: Job): Promise<void> {
//
}
#OnQueueCompleted()
async onCompleted(job: Job): Promise<void> {
//
}
#OnQueueWaiting()
async onWaiting(job: Job): Promise<void> {
//
}
#OnQueueFailed()
onError(job: Job<any>, error: BadRequestException): void {
//
}
i run my function addToQueue() from my controller where i provide a parameters, buuuuuut
it is possible to return any response to the client from the queue about sucessfull or failed job?
If it is possible can someone show how to do it?
thanks for any help
.////////////////////////.
Immediately, there isn't really any way. A queue is there so you can run asynchronous tasks without blocking your main thread or holding up your server in any way. If you were to want to return the result, depending on the job the queue is running, you could be waiting for a few seconds to a few days (a bit of an exaggeration, but it gets the point across). What you could do instead is one of two things (at least that come immediately to mind)
return an Id that you create that relates back to this job. When the job finishes, save the result with that Id to the database. Then, the user can request the status at any time.
Take note of the user who sent in the request, and send an email to that user once the job completes. This will require more integrations, but can get more accurate results over "Ping every so often and we'll tell you if it's done".
Both approaches are valid, and you could even make a combination of the two (probably the best of both worlds so you can save the results and alert the user when it's done).
Imagine that we have an HTML page that fires AJAX requests. We want to make sure that AJAX requests are executed in order. The next AJAX request won't be fired until the previous one completes or errors.
I have tried to model this via a task queue using RxJS concatMap. Each AJAX request is modeled as an Observable. Everything is working great if AJAX request completes successfully, however if it errors, then the next task in the queue is not executed.
Here is an example, that uses setTimeout() to simulate long running async tasks:
function identity(observable) {
return observable;
}
function createTaskQueue() {
var subject= new Rx.Subject();
subject
.concatMap(identity)
.onErrorResumeNext(Rx.Observable.of('error'))
.subscribe(function(data) {
console.log('onNext', data);
},
function(error) {
console.log('onError', error);
});
return {
addTask: function(task) {
subject.next(task);
}
}
}
function createTask(data, delay) {
return Rx.Observable.create(function(obs) {
setTimeout(function() {
obs.next(data);
obs.complete();
}, delay);
});
}
function createErrorTask(data, delay) {
return Rx.Observable.create(function(obs) {
setTimeout(function() {
obs.error('Error: ' + data);
obs.complete();
}, delay);
});
}
var taskQueue = createTaskQueue();
taskQueue.addTask(createTask(11, 500))
taskQueue.addTask(createTask(22, 200));
taskQueue.addTask(createErrorTask(33, 1000));
taskQueue.addTask(createTask(44, 300));
taskQueue.addTask(createErrorTask(55, 300));
taskQueue.addTask(createTask(66, 300));
Here is an executable example: https://jsfiddle.net/artur_ciocanu/s6ftxwnf/.
When I run this code the following is printed to the console:
onNext 11
onNext 22
onNext error
Which is expected, but I wonder why the other tasks like 44, 55, etc are not executed.
I am pretty sure I am doing something stupid with onErrorResumeNext() or may be the whole approach is totally wrong.
Any help is very much appreciated.
If you read the documentation of onErrorResumeNext,
Continues an observable sequence that is terminated normally or by an
exception with the next observable sequence or Promise.
What that means is that when your source observable will encounter an error, it will switch to whatever you passed to onErrorResumeNext. What happens here is that Rx.of(...) terminates immediately after emitting its value. Hence the behavior you observe.
So in short, you don't want onErrorResumeNext here.
You could instead .catch(...) the stream which could emit an error. So, something like :
subject
.concatMap(obs => obs.catch(Rx.Observable.of('error')))
.subscribe(...)
the idea of an error in observables is the same as in regular function. Meaning if you throw an error in a regular function - function will not return anything. The same is with observables - if observable emits an error that means stream is completed and no more values is coming. So yes it is fundamentally wrong.
The better (right) approach would be to to have a stream of responses where next value can be either successful response or and error response. And if you need to separate them you can split responses stream into two successful/error responses later on.
Hope that helps.
Is it possible, in node.js, to make an asynchronous call that times out if it takes too long (or doesn't complete) and triggers a default callback?
The details:
I have a node.js server that receives a request and then makes multiple requests asynchronously behind the scenes, before responding. The basic issue is covered by an existing question, but some of these calls are considered 'nice to have'. What I mean is that if we get the response back, then it enhances the response to the client, but if they take too long to respond it is better to respond to the client in a timely manner than with those responses.
At the same time this approach would allow to protect against services that simply aren't completing or failing, while allowing the main thread of operation to respond.
You can think of this in the same way as a Google search that has one core set of results, but provides extra responses based on other behind the scenes queries.
If its simple just use setTimout
app.get('/', function (req, res) {
var result = {};
// populate object
http.get('http://www.google.com/index.html', (res) => {
result.property = response;
return res.send(result);
});
// if we havent returned within a second, return without data
setTimeout(function(){
return res.send(result);
}, 1000);
});
Edit: as mentioned by peteb i forgot to check to see if we already sent. This can be accomplished by using res.headerSent or by maintaining a 'sent' value yourself. I also noticed res variable was being reassigned
app.get('/', function (req, res) {
var result = {};
// populate object
http.get('http://www.google.com/index.html', (httpResponse) => {
result.property = httpResponse;
if(!res.headersSent){
res.send(result);
}
});
// if we havent returned within a second, return without data
setTimeout(function(){
if(!res.headersSent){
res.send(result);
}
}, 1000);
});
Check this example of timeout callback https://github.com/jakubknejzlik/node-timeout-callback/blob/master/index.js
You could modify it to do action if time's out or just simply catch error.
You can try using a timeout. For example using the setTimeout() method:
Setup a timeout handler: var timeOutX = setTimeout(function…
Set that variable to null: timeOutX = NULL (to indicate that the timeout has been fired)
Then execute your callback function with one argument (error handling): callback({error:'The async request timed out'});
You add the time for your timeout function, for example 3 seconds
Something like this:
var timeoutX = setTimeout(function() {
timeOutX = null;
yourCallbackFunction({error:'The async request timed out'});
}, 3000);
With that set, you can then call your async function and you put a timeout check to make sure that your timeout handler didn’t fire yet.
Finally, before you run your callback function, you must clear that scheduled timeout handler using the clearTimeout() method.
Something like this:
yourAsyncFunction(yourArguments, function() {
if (timeOutX) {
clearTimeout(timeOutX);
yourCallbackFunction();
}
});