I'm a bit stuck on how I would implement a function to poll a certain HTTP endpoint until I get a certain result (i.e. the endpoint updates), but I would like it to send a request every x seconds and when the first one with the 'correct' response comes back, I'd like to halt/throw away every other process and work with the response that I've gotten.
This means that there can be more than one "in flight" request at any given time and I'm not sure how to implement this, i.e. not wait for a response from a prior request before making another request.
I'm using the request module which is already async in nature but I'm not sure how i would "fire off" requests every x seconds without waiting on previous ones to complete.
You can use setInterval. Something like this could work:
function pollIt() {
return new Promise(resolve => {
let done = false;
const interval = setInterval(async () => {
if(done) {
return;
}
const req = await fetch('/test');
if(done) {
return;
}
if(req.status === 200) {// assuming HTTP 200 means "OK", could be something else
done = true;
clearInterval(interval);
resolve(await req.json());
}
}, 1000)
});
}
doAgain = true;
function doRequest() {
request('YOUR END POINT ', function(error, response, body) {
if (body == "WHAT I WANT IT TO BE") {
doAgain = false;
if (doAgain) {
// DO WHAT YOU WANT TO DO
}
} else {
// dont do anything
}
});
}
var interval = setInterval(function() {
if (doAgain) {
doRequest()
} else {
clearInterval(interval)
}
}, 100);
Related
I'm using websocket to send and receive data (up to 30 small messages per seconds). I want the client to send a websocket payload and wait for a specific message from the server.
Flow:
The client send a request
It also store the requestId (163) in waitingResponse object as a new object with sent timestamp
waitingResponse = {
163: { sent: 1583253453549 }
}
When the server response, another function validate the payload and then append the result to that request object
waitingResponse = {
163: { sent: 1583253453549, action: "none" }
}
The client checks every x ms that object for the action key
I have a function sendPayload that sends the payload and then await for a value from awaitResponse (the function below). Right now this function doesn't work. I tried making 2 separate function, one that would be the setTimeout timer, the other was the promise. I also tried having both in the same function and decide if it was the loop or the promise with the original argument you can see below. Now I'm thinking that function should always return a promise even in the loop but I cannot seem to make that work with a timer and I'm afraid of the cost of multiple promise in one another. Let's say I check for a response every 5 ms and the timeout it 2000ms. That is a lot of promises.
public async sendPayload(details) {
console.log("sendPlayload", details);
this.waitingResponse[details.requestId] = { sent: +new Date() };
if (this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify(details));
}
const bindAwaitResponse = this.awaitResponse.bind(this);
return new Promise(async function (resolve, reject) {
const result = await bindAwaitResponse(details.requestId, true);
console.log("RES", result);
console.info("Time took", (+new Date() - result.sent) / 1000);
resolve(result);
});
}
public async awaitResponse(requestId, original) {
// console.log(requestId, "awaitResponse")
return new Promise((resolve, reject) => {
// Is it a valid queued request
if (this.waitingResponse[requestId]) {
// Do we have an answer?
if (this.waitingResponse[requestId].hasOwnProperty("action")) {
console.log(requestId, "Got a response");
const tmp = this.waitingResponse[requestId];
delete this.waitingResponse[requestId]; // Cleanup
resolve(tmp);
} else {
// No answer yet from remote server
// console.log("no answer: ", JSON.stringify(this.waitingResponse));
// Check if request took too long
if (+new Date() - this.waitingResponse[requestId].sent > 5000) { // TODO: Option for time out
console.warn(requestId, "Request timed out");
// Timed out, result took too long
// TODO: Option, default action when timed out
delete this.waitingResponse[requestId]; // Cleanup
resolve({
action: "to" // For now, just sent a timeout action, maybe the default action should be outside of the Network class?
})
} else {
// console.log(requestId, "Still waiting for results");
console.log(JSON.stringify(this.waitingResponse));
// Still waiting, after x ms, recall function
return setTimeout(async () => { resolve(await this.awaitResponse(requestId, false)); }, 200);
}
}
}
});
}
private async processMessage(msg) {
console.log("WS received Message", JSON.stringify(msg.data));
console.log("Current: ", JSON.stringify(this.waitingResponse));
let data = JSON.parse(msg.data);
// console.log("Received: ", data);
if (data.hasOwnProperty("requestId") && this.waitingResponse[data.requestId]) {
// console.log("processMessage ID found");
this.waitingResponse[data.requestId] = { ...data, ...this.waitingResponse[data.requestId] };
}
}
Note: I put the websocket tag below because I looked hard for that. Maybe I came across the solution without even realising it, but if you have better tags for this question to be found easier, please edit them :)
Yeah, you're mixing a lot of callback style functions with intermediate promises and async/await. Don't do polling when waiting for a response! Instead, when writing a queue, put the resolve function itself in the queue so that you can directly fulfill/reject the respective promise from the response handler.
In your case:
public async sendPayload(details) {
const request = this.waitingResponse[details.requestId] = { sent: +new Date() };
try {
if (this.socket.readyState === WebSocket.OPEN) {
this.socket.send(JSON.stringify(details));
}
const result = await new Promise(function(resolve) {
request.resolve = resolve;
setTimeout(() => {
reject(new Error('Timeout')); // or resolve({action: "to"}), or whatever
}, 5000);
});
console.info("Time took", (+new Date() - request.sent) / 1000);
return result; // or {...request, ...result} if you care
} finally {
delete this.waitingResponse[details.requestId];
}
}
private async processMessage(msg) {
let data = JSON.parse(msg.data);
if (data.hasOwnProperty("requestId") {
const request = this.waitingResponse[data.requestId]
if (request)
request.resolve(data)
else
console.warn("Got data but found no associated request, already timed out?", data)
} else {
console.warn("Got data without request id", data);
}
}
You might even do away with the request object altogether and only store the resolve function itself, if the processMessage function does not need any details about the request.
I am trying to make an API call and I want it to repeat every 2 seconds. However I am afraid that if the system doesn't get a request back in 2 seconds, that it will build up requests and keep trying to send them. How can I prevent this?
Here is the action I am trying to fetch:
const getMachineAction = async () => {
try {
const response = await fetch( 'https://localhost:55620/api/machine/');
if (response.status === 200) {
console.log("Machine successfully found.");
const myJson = await response.json(); //extract JSON from the http response
console.log(myJson);
} else {
console.log("not a 200");
}
} catch (err) {
// catches errors both in fetch and response.json
console.log(err);
}
};
And then I call it with a setInterval.
function ping() {
setInterval(
getMachineAction(),
2000
);
}
I have thought of doing some promise like structure in the setInterval to make sure that the fetch had worked and completed, but couldn't get it working.
The Promise.all() Solution
This solution ensures that you don't miss-out on 2 sec delay requirement AND also don't fire a call when another network call is underway.
function callme(){
//This promise will resolve when the network call succeeds
//Feel free to make a REST fetch using promises and assign it to networkPromise
var networkPromise = fetch('https://jsonplaceholder.typicode.com/todos/1');
//This promise will resolve when 2 seconds have passed
var timeOutPromise = new Promise(function(resolve, reject) {
// 2 Second delay
setTimeout(resolve, 2000, 'Timeout Done');
});
Promise.all(
[networkPromise, timeOutPromise]).then(function(values) {
console.log("Atleast 2 secs + TTL (Network/server)");
//Repeat
callme();
});
}
callme();
Note: This takes care of the bad case definition as requested by the author of the question:
"the "bad case" (i.e. it takes longer than 2 seconds) is I want it to skip that request, and then send a single new one. So at 0 seconds the request sends. It takes 3 seconds to execute, then 2 seconds later (at 5) it should reexcute. So it just extends the time until it sends."
You could add a finally to your try/catch with a setTimeout instead of using your setInterval.
Note that long polling like this creates lot more server load than using websockets which themselves are a lot more real time
const getMachineAction = async () => {
try {
const response = await fetch( 'https://localhost:55620/api/machine/');
if (response.status === 200) {
console.log("Machine successfully found.");
const myJson = await response.json(); //extract JSON from the http response
console.log(myJson);
} else {
console.log("not a 200");
}
} catch (err) {
// catches errors both in fetch and response.json
console.log(err);
} finally {
// do it again in 2 seconds
setTimeout(getMachineAction , 2000);
}
};
getMachineAction()
Simple! Just store whether it's currently making a request, and store whether the timer has tripped without sending a new request.
let in_progress = false;
let missed_request = false;
const getMachineAction = async () => {
if (in_progress) {
missed_request = true;
return;
}
in_progress = true;
try {
const response = await fetch('https://localhost:55620/api/machine/');
if (missed_request) {
missed_request = false;
setTimeout(getMachineAction, 0);
}
if (response.status === 200) {
console.log("Machine successfully found.");
const myJson = await response.json(); //extract JSON from the http response
console.log(myJson);
} else {
console.log("not a 200");
}
} catch (err) {
// catches errors both in fetch and response.json
console.log(err);
} finally {
in_progress = false;
}
};
To start the interval, you need to omit the ():
setInterval(getMachineAction, 2000);
I'm currently starting with NodeJS and I want to run the Same function multiple times at once with different Arguments, but I am not sure how.
From my current experience NodeJS/JavaScript in general doesn't wait for a function to return, but in my case it does which really confuses me.
My current code looks something like this:
XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
start=function(values){
max=5
started=0
finished=0
maxReachedBefore=false
finishedStarting=false
result=[]
interval = setInterval(function(){
if(!finishedStarting){
if((started-finished)>= max) {
if(!maxReachedBefore){
console.log("Max Reached!")
maxReachedBefore=true
}
return;
}
maxReachedBefore=false
getValue(values[started])
console.log("Started getValue")
started++
} else {
if (started<=finished){
clearInterval(interval)
console.log("Finished!")
}
}
},10)
}
getValue=function(value){
var request=new XMLHttpRequest()
request.open("get","MY_WEBSITE_HERE.COM/"+value, false)
request.send()
result=result.concat(request.responseText)
console.log("Finished getValue")
finished++
}
start(["a","medium","sized","list","of","values","that","i","want","to","get","the","corresponding","values","to"])
Now I would expect this to Keep Starting getValue with the different values while max is not reached -> getValue is running less than 6 times, but this isn't the case. getValue is started One-by-One, the next one only starting after the last one finished.
Can someone explain why this is happening / Help me change it?
Please have mercy, I'm relatively new to NodeJS
You are sending a synchronous request to server. So the function will wait for the response from the server.
request.open("get","MY_WEBSITE_HERE.COM/"+value, false)
The last parameter boolean specifies whether the call is sync or async.
request.open("get","MY_WEBSITE_HERE.COM/"+value, true)
This will send asynchronous call, and in the callback function add the console statements of finished get value.
var finished = 0;
var result=[];
start=function(values){
max=5
started=0
maxReachedBefore=false
finishedStarting=false
interval = setInterval(function(){
if(!finishedStarting){
if((started-finished)>= max) {
if(!maxReachedBefore){
console.log("Max Reached!")
maxReachedBefore=true;
finishedStarting = true;
}
return;
}
maxReachedBefore=false;
console.log("Started getValue " + values[started]);
getValue(values[started]);
started++;
} else {
if (started<=finished){
clearInterval(interval)
console.log("Finished!")
}
}
},10);
}
getValue=function(value){
var request=new XMLHttpRequest()
request.open("get","MY_WEBSITE_HERE.COM/"+value, true);
request.onreadystatechange = function () {
if(request.readyState === 4 && request.status === 200) {
result=result.concat(request.responseText);
console.log("Finished getValue " + value)
finished++;
}
};
request.send();
console.log("request sent");
}
I am a beginner in javascript, and I'm trying to figure out why my while loop won't actually loop more than once, even though the condition is always met.
I have a function sending an API request:
var get_status = function(trid, count) {
console.log(count);
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
$http(req).success(function(data) {
if (data.transaction_status != 'Pending') {
// do something with the data
console.log('true');
return true;
}
else {
console.log('False');
return false;
}
}).error(function(data) {
// show an error popup
console.log('true');
return true;
})
}
};
I want to call this function until it returns true, so I call it this way:
var count = 0;
while (get_status(id, count) === false) {
count += 1;
}
The count variable is just added to see how many times it loops, it stays at 0 even though 'False' is displayed in the console.
Is there some behaviour I am misunderstanding here?
EDIT I understand why this won't work. My intention here is to display an iframe as long as the transaction status is pending. I thought of continually sending a request until the transaction status is something other then 'Pending', but I am aware there are more optimal ways.
Your get_status() function does not return a value. Thus, it's return value is undefined which is falsey so your while() loop stops after the very first iteration.
The return statements you do have in your code are inside of callbacks and have nothing to do with the return value of get_status().
What you are attempting to do is generally not a good design. It appears that you want to run a given Ajax call over and over with no delay until you get the answer you want. This will potentially hammer the destination server.
If you describe the problem you're really trying to solve, we could help come up with a better way to do this. Worst case, you could poll the server with a time delay between requests.
If you wanted to poll every so often, you could do something like this:
function get_status(trid, count) {
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
return $http(req).then(function(data) {
return data.transaction_status;
});
}
function poll_status(callback) {
function next() {
get_status(...).then(function(status) {
if (status === "Pending") {
// poll once every two seconds
setTimeout(next, 2000);
} else {
// status is no longer pending, so call the callback and pass it the status
callback(status);
}
}, function(err) {
callback(err);
});
}
next();
}
poll_status(function(result) {
// done polling here, status no longer Pending
});
This is not the correct way to deals with async calls, I'd create a recursive function which will call itself. (in this case get_status should return a promise)
Code
var count = 0, id = 1;//id should be some value
(function myCall(promise){}
promise.then(function(data){
count += 1;
if(data)
myCall(get_status(id, count)); //call function on conditon
});
}(get_status(id, count))
Method(Returning Promise)
var get_status = function(trid, count) {
console.log(count);
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
//returning promise here
return $http(req).then(function(response) {
var data = response.data;
if (data.transaction_status != 'Pending') {
// do something with the data
console.log('true');
return true; //resolves the promise
}
else {
console.log('False');
return false; //resolves the promise
}
}, function(data) {
// show an error popup
console.log('true');
return true;
})
}
};
You're trying to return from within an asynchronous callback, which won't work, unfortunately. Instead you'll want a module like async, specifically whilst.
var count = 0;
var outcome = false;
async.whilst(
function () { outcome = false; },
function (callback) {
count++;
// Your code here, setting outcome instead of returning
var req = {
method: 'GET',
url: 'theUrlHere',
headers: {'headers'}
}
$http(req).success(function(data) {
if (data.transaction_status != 'Pending') {
outcome = true;
callback();
}
else {
outcome = false
callback();
}
}).error(function(data) {
outcome = true;
callback();
})
},
function (err) {
// All done!
}
);
But really the behavior you're looking for is probably checking on a status at pre-defined intervals. In this case, adapting the code
var count = 0;
var outcome = false;
async.whilst(
function () { outcome = false; },
function (callback) {
count++;
// Your request stuff.
setTimeout(function () {
callback();
}, 1000); // Waits one second to begin next request
},
function (err) {
// All done!
}
);
I am writing code for JavaScript.In which I am trying to check the remote asp.net page(aspx) connection using AJAX.But also I want to check the condition that, this call will continue for 2 Minute only and with 10 sec time intervals.
not that but like that logic I am thinking,
If flag=true
if seconds < 120
setInterval("GetFeed()", 2000);
can anybody please help me for that.
Here is my code of Connection check,
var learnerUniqueID1;
var flag='true';
function fnCheckConnectivity(coursId)
{
//here will the remote page url
var url = 'http://<%=System.Web.HttpContext.Current.Request.UrlReferrer.Host+System.Web.HttpContext.Current.Request.ApplicationPath%>/TestConn.aspx';
//alert(url);
if (window.XMLHttpRequest) {
learnerUniqueID1 = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
learnerUniqueID1 = new ActiveXObject("Microsoft.XMLHTTP");
}
else
{
alert("Your browser does not support XMLHTTP!");
}
learnerUniqueID1.open("POST", url, true);
learnerUniqueID1.onreadystatechange = callbackZone;
learnerUniqueID1.send(null);
}
function callbackZone()
{
if (learnerUniqueID1.readyState == 4)
{
if (learnerUniqueID1.status == 200)
{
//update the HTML DOM based on whether or not message is valid
parseMessageZone();
}
else
{
flag='false';
alert('We have detected a break in your web connection, \n please login again to continue with your session');
}
}
}
function parseMessageZone()
{
var result = learnerUniqueID1.responseText;
}
function makePayment(obj)
{
try
{
var Id = obj.attributes["rel"].value
//alert(Id);
var res=confirm('Want to Continue.');
if(res == true)
{
startRequest(Id);
//return true;
}
else
{
return false;
}
}
catch(Error)
{
}
}
function startRequest(Id)
{
var milliseconds = 10000;
currentDate = new Date();
// start request
pollRequest(milliseconds, false, currentDate, 120000,Id);
}
function pollRequest(milliseconds, finshed, date, timeout,Id)
{
if((new Date()).getTime() > date.getTime()+timeout)
{
// 2-minute timeout passed
return;
}
if(!finished){
setTimeout(function(){
if(//check backend to see if finished) //which method will go here
{
fnCheckConnectivity(coursId);
pollRequest(milliseconds, true, date, timeout,Id);
}
else{
pollRequest(milliseconds, false, date, timeout,Id)
}
}, milliseconds);
return;
}
// when code reaches here, request has finished
}
I'm not sure I understand correctly, but if you are trying to poll the status of a request you made every ten seconds, why not try something like this?
function startRequest(){
var milliseconds = 10000;
currentDate = new Date();
timeout = 120000;
// start request
pollRequest(milliseconds, false, currentDate, timeout);
}
function pollRequest(milliseconds, finshed, date, timeout){
if((new Date()).getTime() > date.getTime()+timeout){
// 2-minute timeout passed
return;
}
if(!finished){
setTimeout(function(){
if(//check backend to see if finished){
pollRequest(milliseconds, true, date, timeout);
}
else{
pollRequest(milliseconds, false, date, timeout)
}
}, milliseconds);
return;
}
// when code reaches here, request has finished
}
Please note this is a recursive function and browsers have a recursion limit. Also, regarding the 2 minute timeout, you can either set the timeout as an ajax prefilter or add a variable that is added on each recursion.
If I understand correctly to want to do a task for 2 minutes every 10 seconds.
var interval = setInterval(function(){
// do stuff
},10000); // execute every 10 seconds
setTimeout(function(){
clearInterval(interval);
},120000); // Remove interval after 2 minutes
You don't want a script running continuously on a server/client. You should look up chronjob for linux or scheduler for windows.
I didn't give you a proper solution, just an alternative. For what you seem to want to do, however, this seems like the right tool.
Instead of a push request that you're describing, in which the client user waits for a certain amount of time and then makes a request, you should use a pull request.
To do that, you can make an AJAX call to the sever, and then the sever can wait (and possibly do something else while waiting) and then return something to the user when data is ready.