Proper way to use setInterval on multiple HTTP requests with Node - javascript

Say you have a list which contains 3 ~ 5 urls. Requests should be made every 5 seconds, one by one. I wrote code like :
setInterval(() => {
array.foreach(element => {
request
.get(element.url)
.on('response', function(response) {
console.log(response);
}
});
}, 5000)
But because the enqueueing setInterval is faster than executing the request, the .on callback is not responding. I think, before enqueueing setInterval, I must ensure that the I've finished .on callback.
How can I do that?

Recommend you the Async.js module.
If you want a async loop to solve, you can:
var i = 0;
async.whilst(
function() { return i < array.length; },
function(next) {
var elemet = array[i++];
request
.get(element.url)
.on('response', function(response) {
console.log(response);
// Do next while 5s passed
setTimeout(next, 5000);
}
}
},
function (err, n) {
// All Task over
}
);
Or if you want try some concurrency:
// create a queue object with concurrency 2
var q = async.queue(function(task, next) {
console.log('request ' + task.url);
request
.get(task.url)
.on('response', function(response) {
console.log(response);
next();
}
}
}, 2);
// assign a callback
q.drain = function() {
console.log('all task have been processed');
};
// Push a request task to the queue every 5s
setInterval(function () {
let task = array.shift()
if (task) {
q.push(task, function(err) {
console.log('finished processing', task.url);
});
}
}, 5000);

Can you please try this?
var requestArray = [
{
url: 'some URL number 1'
},
{
url: 'some URL number 2'
},
{
url: 'some URL number 2'
}
]
function makeRequests(){
for(var i=0; i<requestArray.length; i++){
request.get(requestArray[i].url)
.on('response',function(response){
console.log(response);
if(i == requestArray.length - 1)//You can check any condition here if you want to stop executing get requests{
makeRequests();
}
})
}
}

Related

Using exponential backoff on chained requests

I have chained requests on GA API, meaning the second requests uses result from the first request. I wanted to use exponentialBackoff() function to retry if there is an error (status 429 for example).
My question is, what would be the best way of checking that both of the requests were successful and only repeat the one which wasn't. In my example, both of the requests get called again even if only the second one is not successful.
async function exponentialBackoff(toTry, max, delay) {
console.log('max', max, 'next delay', delay);
try {
let response = await toTry()
console.log(response);
} catch (e) {
if (max > 0) {
setTimeout(function () {
exponentialBackoff(toTry, --max, delay * 2);
}, delay);
} else {
console.log('we give up');
}
}
}
propertyArr.forEach(async function (e, i, a) {
await exponentialBackoff(async function () {
let response = await gapi.client.request({
path: 'https://analyticsadmin.googleapis.com/v1beta/properties',
method: 'POST',
body: e
})
return await gapi.client.request({
path: 'https://analyticsadmin.googleapis.com/v1beta/' + response.result.name + '/dataStreams',
body: dataStreamArr[i],
method: 'POST'
})
}, 10, 30)
})

Promises in JavaScript not executing propertly

I have this series of steps that needs to be completed in order:
Validate an object
Fetch an image URL using Bing's image search API
Add the found URLs to the object
Make a post request and send the object
A sample object looks like:
`{
options: [{
text: 'Pizza',
votes: 0,
imageURL: ""
}, {
text: 'Hot Dog',
votes: 0,
imageURL: ""
}]
};`
Because the order in this series, I am using promises to make sure everything goes in the order specified above. So far I have:
function validatePoll() {
var isValid = true;
for (var i = 0; i < $scope.poll.options.length; i++) {
if (!$scope.poll.options[i].text) {
isValid = false;
break;
}
}
return isValid;
}
let promiseURL = function(searchTerm) {
return new Promise(function(resolve, reject) {
$http.get('https://api.cognitive.microsoft.com/bing/v5.0/images/search?q=' + searchTerm + '&count=1&offset=0&mkt=en-us&safeSearch=Strict', {
headers: {
'Ocp-Apim-Subscription-Key': 'XXXXXXXXXXXXXXXXXXXXXXXXXX'
}
}).success(function(response) {
console.log(response);
resolve(response);
}).error(function (err, status) {
reject(err);
})
})
};
let fetchImageURL = function() {
for(var i = 0; i < $scope.poll.options.length; i++) {
console.log(promiseURL($scope.poll.options[i].text));
}
}
$scope.submitChoice = function() {
var isValid = validatePoll();
if(isValid) {
fetchImageURL();
} else {
console.log("Not Valid Poll");
}
}
But what ends up happening is the console.log(promiseURL($scope.poll.options[i].text)); in the fetchImageURL returns an unresolved promise instead of the response string I want instead. How might I be able to fix the code to ensure that:
A call to promiseURL is made with the proper argument
A response is received and can be parsed
The parsed information can be added to the imageURL property in the polls object
You're seeing the printout of the Promise because you're printing out the promise. You need to be handling the promise resolve/reject. What you should be calling is:
promiseURL($scope.poll.options[i].text)
.then(function(data){
console.log("Success",data)
})
.catch(function(error){
console.log("Error",error")
})
Promises are usually used as follows:
promise.then(function(result) {
console.log(result); // "Stuff worked!"
}, function(err) {
console.log(err); // Error: "It broke"
});
Where the promise itself looks like:
var promise = new Promise(function(resolve, reject) {
// do a thing, possibly async, then…
if (/* everything turned out fine */) {
resolve("Stuff worked!");
}
else {
reject(Error("It broke"));
}
});
It doesn't look like, in your loop, you're calling then on promiseURL($scope.poll.options[i].text). Try doing something like:
for(var i = 0; i < $scope.poll.options.length; i++) {
promiseURL($scope.poll.options[i].text).then(function(result) {
console.log(result); // "Stuff worked!"
});
}
See https://developers.google.com/web/fundamentals/getting-started/primers/promises for a great primer on promises.

Node JS | Loop which waits callback?

I have a array which I have to loop through. I can't use for loop because it's asynchronous and it has callback in. I would have to use a loop which waits for callback. Is that possible?
Code:
if ( bots[1].getInventory().getItems().length < capacity ){
var executed = false;
bots[1].createDeposit({
steamid: req.query.steamid,
token: req.query.token,
itemsFromThem: uniqueItems,
message: req.query.message,
callback: function( err, dbTradeId ){
if ( !executed ){
executed = true;
if ( !err && dbTradeId ){
res.json({ result: 1, dbTradeId: dbTradeId });
} else {
console.log('» Tried to create deposit, but',err);
myMessages.push("Problem");
res.json({ error: err });
}
}
}
});
} else {
console.log('» Tried to create deposit, but no bot found(2)');
myMessages.push("Available bot not found(2)");
}
My question is not a duplicate because, I don't want it to go through every item in the array. Only until the successful callback has been executed.
Regards
You must take a look at async#each .It allows you to run async calls against a list of array or in a loop and gives you a place to run a method when all of the async calls are done.
// Should give you an idea how to use it
async.each(bots, function (item, callback) {
if ( item.getInventory().getItems().length < capacity ){
var executed = false;
item.createDeposit({
steamid: req.query.steamid,
token: req.query.token,
itemsFromThem: uniqueItems,
message: req.query.message,
callback: function( err, dbTradeId ){
if ( !executed ){
executed = true;
if ( !err && dbTradeId ){
callback(null, { result: 1, dbTradeId: dbTradeId });
// res.json();
} else {
console.log('» Tried to create deposit, but',err);
myMessages.push("Problem");
callback(err);
}
}
}
});
} else {
console.log('» Tried to create deposit, but no bot found(2)');
myMessages.push("Available bot not found(2)");
}
}, function (err) {
console.log('All done');
});
You can create another array of promises and then use Promise.all to wait them for completion. I don't known the source of what you have to iterate, but let's assume that you want to make an http connection:
const yourThingies = [
"http://a-server.com/",
"http://www.another.com/"
//And so on...
];
let promises = [];
yourThingies.forEach( (url) => {
const p = new Promise((resolve, reject) => {
//Call this on resolve, or use something that returns a promise.
resolve("your data");
});
promises.push(p);
});
Promise.all(promises).then((solvedPromises) => {
//Do things once all is done
solvedPromises.forEach((solv) => {
//So on...
});
});
Further information about Promise Promise MDN Docs
EDIT:
Note: When I've answered there was no code, I'll try to edit.

There is a default timeout with q.allSettled?

I'm working on a NodeJS app who is fetch lot of information from differents API.
During the biggest fetching, i have some issues with my promises.
I guess I launch too many requests and too fastly ...
var promises = [];
list_consumptions.forEach(function (item)
{
item.consumptions.forEach(function (data)
{
promises.push(getDetails(item.line, data));
});
});
In the getDetails()
function getDetails(line, item)
{
var deferred = Q.defer();
var result = [];
ovh.request('GET', '/telephony/*******/service/' + line + '/voiceConsumption/' + item, function (err, fetched_data)
{
if (!err)
{
result = {
'line': line,
'consumption_id': item,
'type': fetched_data.wayType,
'calling': fetched_data.calling,
'called': fetched_data.called,
'plan_type': fetched_data.planType,
'destination_type': fetched_data.destinationType,
'date': fetched_data.creationDatetime,
'duration': fetched_data.duration,
'price': fetched_data.priceWithoutTax
};
// console.log("RESULT: ",result);
deferred.resolve(result);
}
else
{
deferred.reject(err);
console.log(err);
}
});
return deferred.promise;
}
After the loop :
Q.allSettled(promises).done(function(final_result)
{
final_result.forEach(function (promise_fetch){
if (promise_fetch.state != 'fulfilled')
{
console.log("ERREUR Merge");
}
else
{
all_consumptions.push(promise_fetch.value);
}
deferred.resolve(all_consumptions);
});
return deferred.promise;
});
With this code, i've got exclusively log error : 400
I'd try to slow down my loop with some setTimeOut, in this case, the fetch succeed but my q.allSettled is jumped... I'm really lost...
Any ideas to improve my promises handles / loop handles ?
I'm pretty sure that you already know, but it's my first week of JS... and nodeJS.
Thanks a lot for your help...
You could use a promise-loop, like this:
function pwhile(condition, body) {
var done = Q.defer();
function loop() {
if (!condition())
return done.resolve();
Q.when(body(), loop, done.reject);
}
Q.nextTick(loop);
return done.promise;
}
And then, your code would be:
list_consumptions.forEach(function (item) {
var i = 0;
pwhile(function() { return i < item.consumptions.length; }, function() {
i++;
return getDetails(item.line, data)
.then(function(res) {
all_consumptions.push(res);
})
.catch(function(reason) {
console.log("ERREUR Merge");
});
}).then(function() {
// do something with all_consumptions here
});
});

How to use setTimeout in Node.JS within a synchronous loop?

The goal I'm trying to achieve is a client that constantly sends out data in timed intervals. I need it to run indefinitely. Basically a simulator/test type client.
I'm having issues with setTimeout since it is an asynchronous function that is called within a synchronous loop. So the result is that all the entries from the data.json file are outputted at the same time.
But what i'm looking for is:
output data
wait 10s
output data
wait 10s
...
app.js:
var async = require('async');
var jsonfile = require('./data.json');
function sendDataAndWait (data) {
setTimeout(function() {
console.log(data);
//other code
}, 10000);
}
// I want this to run indefinitely, hence the async.whilst
async.whilst(
function () { return true; },
function (callback) {
async.eachSeries(jsonfile.data, function (item, callback) {
sendDataAndWait(item);
callback();
}), function(err) {};
setTimeout(callback, 30000);
},
function(err) {console.log('execution finished');}
);
You should pass the callback function:
function sendDataAndWait (data, callback) {
setTimeout(function() {
console.log(data);
callback();
//other code
}, 10000);
}
// I want this to run indefinitely, hence the async.whilst
async.whilst(
function () { return true; },
function (callback) {
async.eachSeries(jsonfile.data, function (item, callback) {
sendDataAndWait(item, callback);
}), function(err) {};
// setTimeout(callback, 30000);
},
function(err) {console.log('execution finished');}
);

Categories