How to repeat a "request" until success? NODEJS - javascript

I checked a few thread in StackOverflow, but nothing works for me.
I have this request call and I need it to try to send the request until it succeed (but if it fails, it has to wait at least 3 seconds):
sortingKeywords.sortVerifiedPhrase = function(phrase) {
var URL = "an API URL"+phrase; //<== Obviously that in my program it has an actual API URL
request(URL, function(error, response, body) {
if(!error && response.statusCode == 200) {
var keyword = JSON.parse(body);
if(sortingKeywords.isKeyMeetRequirements(keyword)){ //Check if the data is up to a certain criteria
sortingKeywords.addKeyToKeywordsCollection(keyword); //Adding to the DB
} else {
console.log("doesn't meet rquirement");
}
} else {
console.log(phrase);
console.log("Error: "+ error);
}
});
};
Here's the weird part, if I call the same phrases in a row from the browser, it works almost without errors (it usually states: rate limit time esceeded).
Appreciate your help.
Thanks in advance.

Here is a working program that I've written for this request. It sends the request via a function, if the request fails, it returns error handler and calls the function again.
If the function succeeds the program returns a promise and quits executing.
NOTE: if you enter an invalid url the programs exits right away, that's something that's have to do with request module, which I like to be honest. It lets you know that you have an invalid url. So you must include https:// or http:// in the url
var request = require('request');
var myReq;
//our request function
function sendTheReq(){
myReq = request.get({
url: 'http://www.google.com/',
json: true
}, (err, res, data) => {
if (err) {
console.log('Error:', err)
} else if (res.statusCode !== 200) {
console.log('Status:', res.statusCode)
} else {
// data is already parsed as JSON:
//console.log(data);
}
})
}
sendTheReq();
//promise function
function resolveWhenDone(x) {
return new Promise(resolve => {
myReq.on('end', function(){
resolve(x)
})
myReq.on('error', function(err){
console.log('there was an error: ---Trying again');
sendTheReq(); //sending the request again
f1(); //starting the promise again
})
});
}
//success handler
async function f1() {
var x = await resolveWhenDone(100);
if(x == 100){
console.log("request has been completed");
//handle other stuff
}
}
f1();

On error run this code
setTimeout(function(){}, 3000);
See this https://www.w3schools.com/jsref/met_win_settimeout.asp
Also you can make a code like this
var func1 = function(){};
setTimeout(func1, 3000);

Related

How can I add a setTimeOut() to run a function and insert data multiple times?

A few days ago I did a project and I had some problems, which where solved in this question, let me try to resume it.
I need to insert multiple objects into a DB in SQLServer, for that, I did a function that loops another function, which opens a connection, inserts and closes the connection, then, repeats it over and over again.
It worked fine, till today that was tested in a collegue PC, in the server of the job, I get this error:
Error: Requests can only be made in the LoggedIn state, not the LoggedInSendingInitialSql state
Error: Requests can only be made in the LoggedIn state, not the SentLogin7WithStandardLogin state
Here's the code we tested (the same in my last question), it works in my PC, but not in the other:
var config = {
...
};
function insertOffice(index) {
var connection = new Connection(config);
connection.on("connect", function (err) {
console.log("Successful connection");
});
connection.connect();
let url = `https://api.openweathermap.org/data/2.5/weather?lat=${offices[index].latjson}&lon=${offices[index].lonjson}&appid=${api_key}&units=metric&lang=sp`;
fetch(url)
.then((response) => { return response.json(); })
.then(function (data) {
var myObject = {
Id_Oficina: offices[index].IdOficina,
...
};
const request = new Request(
"EXEC USP_BI_CSL_insert_reg_RegistroTemperaturaXidOdicina #IdOficina, ...",
function (err) {
if (err) {
console.log("Couldnt insert data (" + index + "), " + err);
} else {
console.log("Data with ID: " + myObject.Id_Oficina +" inserted succesfully(" + index + ").")
}
}
);
request.addParameter("IdOficina", TYPES.SmallInt, myObject.Id_Oficina);
...
request.on("row", function (columns) {
columns.forEach(function (column) {
if (column.value === null) {
console.log("NULL");
} else {
console.log("Product id of inserted item is " + column.value);
}
});
});
request.on("requestCompleted", function () {
connection.close();
});
connection.execSql(request);
});
}
function functionLooper() {
for (let i = 0; i < offices.length; i++) {
let response = insertOffice(i);
}
}
functionLooper();
So, I thought it would be a good idea to use a setTimeOut, to:
Run functionLooper().
Open connection, insert and close.
Wait a few seconds.
Repeat.
So, I changed to this:
setTimeout(functionLooper, 2000);
function functionLooper() {
for (let i = 0; i < offices.length; i++) {
let response = insertOffice(i);
}
}
It works, but, as you can see, only waits when I first run it, so tried to make a function that runs setTimeout(functionLooper, 2000); like functionLooper() does, but it didn't work either.
function TimerLooper() {
for (let i = 0; i < offices.length; i++) {
setTimeout(functionLooper, 500);
}
}
function functionLooper() {
for (let i = 0; i < offices.length; i++) {
let response = insertOffice(i);
}
}
TimerLooper();
This shows me this error:
Error: Validation failed for parameter 'Descripcion'. No collation was set by the server for the current connection.
file:///...:/.../.../node_modules/node-fetch/src/index.js:95
reject(new FetchError(request to ${request.url} failed, reason: ${error.message}, 'system', error));
^ FetchError: request to https://api.openweathermap.org/data/2.5/weather?lat=XX&lon=XX&appid=XX&units=metric&lang=sp failed, reason: connect ETIMEDOUT X.X.X.X:X
So, I have some questions
How can I use properly setTimeOut? I did this function based on what I watch here in SO, but I just can't get it and I don't know what I'm doing wrong.
Why it works in my PC and the other don't? Do we have to change some kind of config or something?
Using setTimeOut, is the correct way to solve this problem? if not, what would you suggest me?
Could you do something like:
//edit: not disconnect but end
connection.on("end", function(){
functionLopper(index++)
})
function functionLooper(i) {
if(i<offices.length) insertOffice(i)
}
Edit: according to tidious doc
There is an end event emitted on connection.close()
Event: 'end'
function () { }
The connection has ended. This may be as a result of the client calling close(), the server closing the connection, or a network error.
My suggestion from above
var config = {
...
};
function insertOffice(index) {
var connection = new Connection(config);
connection.on("connect", function (err) {
console.log("Successful connection");
});
connection.connect();
let url = `...`;
fetch(url)
.then((response) => { return response.json(); })
.then(function (data) {
...
});
connection.on("end", function(){
functionLopper(index++)
})
}
function functionLooper(i) {
if(i<offices.length) insertOffice(i)
}
``

Fetch call every 2 seconds, but don't want requests to stack up

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);

NodeJS: Abort request function inside loop

I am creating a electron application that can download multiple pages on a website. I want to be able to stop downloading the pages whenever I hit the stop button by instantly terminating the function. I tried stopping the function by setting it to a new function but because downloading happens in a function loop it is not stopped (I am unsure if this approach would even stop the startDownload function).
Is there a way to easily stop the execution of a function, that does not stop the whole script?
Edit: If there isn't a way to stop the execution of the function, is there a way to send a message to and stop the NodeJS request?
Edit 2: NodeJS requests have an abort method, but I am unsure how to tell the function to abort the request?
startDownload('website.com');
startDownload(url) {
var startAt = 0;
var maxPages = 15;
download(url, startAt, maxPages);
}
download(url, page, maxPages) {
if (page == maxPages) { finishDownload(url); return; }
request(url + '?p=' + page, (error, response, html) => {
downloadPage(html);
download(url, page + 1, maxPages);
}).catch((error) => {
finishDownload(url, 'Failed to download');
});
}
finishDownload(url, error = undefined) {
if (!error) {
alert(url + ' finished downloading');
} else {
alert(url + error);
}
}
$(document).on('click', '#stopDownload', function() {
var downloadFunction = startDownload;
startDownload = function() {return false};
startDownload = downloadFunction;
alert('download stopped by killing function');
});
yes you can stop by
var r = request({uri: 'http://stackoverflow.com' }, function (error, response, body) {
console.log('url requested ') ;
if (!error){
console.log(body);
}
else
{
console.log(error);
}
});
r.abort();

How do i display the javascript error object?

I am using Node.js and Express web application framework and
mysql package from here
https://www.npmjs.com/package/mysql
here's my Parent.js file .. it's my model file
var db = require('../dbconnection');
var Parent = {
findIfParentMobileNumberExists: function (parentmobilenumber, callback) {
db.query('SELECT parentmobilenumber from parents where parentmobilenumber=?', parentmobilenumber, function (err, rows) {
if (err) {
callback(err, null);
}
if (rows.length < 1) {
console.log("rows length less than 1");
callback(err, null);
}
else if (rows.length !== 0) {
console.log("rows length greater than 1");
callback(null, rows[0].parentmobilenumber);
}
else {
}
});
}
module.exports = Parent;
and here's my registerParent.js file .. this is my route
var express = require('express');
var router = express.Router();
var Parent = require('../models/Parent');
router.post('/', function (req, res, next) {
var countrycode = req.body.countrycode;
var parentmobilenumber = (countrycode) + (req.body.inputMobileNumber);
//remove hyphens from mobile number
var parentmobilenumberwithouthyphens =
parentmobilenumber.replace(/-/g, "");
//remove spaces from mobile number
var parentmobilenumberwithouthyphensandspaces =
parentmobilenumberwithouthyphens.replace(/ /g, '');
Parent.findIfParentMobileNumberExists(parentmobilenumberwithouthyphensandspaces, function (err, parentmobilenumberfromdb) {
if (err) {
res.json({registerErrorMessage: err.message});
}
else {
if (parentmobilenumberwithouthyphensandspaces === parentmobilenumberfromdb) {
console.log(parentmobilenumberfromdb);
}
else {
res.json({registerErrorMessage: 'The Mobile Number does not exist in our database'});
}
}
});
});
if i enter a correct mobile number i get the mobile number displayed in a console.log message .. however when i enter an incorrect mobile number which is not in the database the resulting rows.length is < 1
i get the message
rows length less than 1
but i don't understand when this block runs
if (rows.length < 1) {
console.log("rows length less than 1");
callback(err, null);
}
the err object is never displayed .. i.e this block is never run in the router.post function
if (err){
res.json({registerErrorMessage: err.message});
}
so i want to display the err object correctly first ..
and then i want to display an error message that is readable by end-users
how can i do this ?
i tried reading the documentation here
https://github.com/mysqljs/mysql#error-handling
but i really don't understand how to properly execute callbacks.
You don't start http server with express.
Try to insert code below to start listening:
const app = express();
app.listen( insert port here )
At first, you might want to terminate your logic if an error occurs:
if(err){
return callback(err);
}
Now the error is passed to the client side, where you can catch it and display a message. E.g. (using jquery):
$.ajax({
url:"your/backend",
dataType: "json",
success(res){
if(res.code){
return alert("an error ("+res.code+") occured. Sorry :/");
}
//continue with valid response
}
});
However, you may change your API to be more general, e.g. always return an error property, thats either false or the error object:
on error:
res.json({error:err});
on success:
res.json({error:false,data:"whatever"});
So that you could do this on client side:
$.ajax({
url:"your/backend",
dataType: "json",
success(res){
if(res.error){
return alert("an error ("+res.error.code+") occured. Sorry :/");
}
//continue with valid response
alert(res.data);
}
});

Nodejs Request followed by Request - synchronization issue

I'm new to the world of callback hell, and have gotten a little stuck. I'm doing some analytics work, so every time a function is called, I call a function which does a request.post to log the request on our server. In each of those original functions, however, a request.get is made to return some information. So, I am essentially calling a function that makes a request inside another function that needs to make a request after that one. The code itself is a lot more complicated (and by complicated, I mean long and annoying) than I just described, so here's the dumbed-down version:
function myFunction(x, y, callback) {
// variable declarations
postFunction(a, b);
// other logic for the function
request.get(url, function(error, response, body) {
// request code here
callback(somedata, otherFunctionThatDoesntMatterHere(blah));
});
}
function postFunction(a, b) {
// other logic for the function
var dataToSend = "XML HERE";
request.post({
url: url,
body: dataToSend,
headers: {'Content-Type' : 'text/xml'},
function(error, response, body) {
if (error) {
console.log(error);
}
else if (response.statusCode !== 200) {
console.log('Invalid Status Code Returned:', response.statusCode);
}
else if (!error && response.statusCode == 200) {
console.log('POST RESPONSE: ', body);
}
}
);
}
What's happening right now is that the post function doesn't return before the end of the main function. I know this is part of node's asynchronous-ness, but I would like these to be executed synchronously. I am pretty sure I can solve this by adding a callback to the post function, but I'm not sure how. I've tried many different approaches, but I'm afraid I'm not entirely understanding how this all works. Thank you in advance for any help you can provide!
If you truly want it to be synchronous, then yes you would need to add a callback to the postFunction.
For example:
function myFunction(x, y, callback) {
// variable declarations
postFunction(a, b, function(err) {
// other logic for the function
//maybe check if "err" has a value here
request.get(url, function(error, response, body) {
// request code here
callback(somedata, otherFunctionThatDoesntMatterHere(blah));
});
});
}
function postFunction(a, b, next) {
// other logic for the function
var dataToSend = "XML HERE";
request.post({
url: url,
body: dataToSend,
headers: {'Content-Type' : 'text/xml'},
function(error, response, body) {
if (error) {
console.log(error);
next(error);
}
else if (response.statusCode !== 200) {
console.log('Invalid Status Code Returned:', response.statusCode);
next('Invalid Status Code Returned:' + response.statusCode);
}
else if (!error && response.statusCode == 200) {
console.log('POST RESPONSE: ', body);
next(null);
}
}
);
}
This puts the rest of the code you want to fire within the callback of the postFunction, requiring it to complete first before anything else fires. Of course this will increase overall runtime of the function, since it has to now wait before continuing to run. Be careful of locking the event loop if you're doing CPU intensive tasks.
Also, to combat the ugliness of callback hell, you can use something like async-waterfall to chain functions one after another: https://www.npmjs.com/package/async-waterfall

Categories

Resources