There is a default timeout with q.allSettled? - javascript

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

Related

JavaScript callbacks - issues with accessing individual elements of a returned array

Self-taught and struggling a little bit to understand how callbacks work.
My callback handling function cannot access individual elements of the array i.e:
"console.log(ResultsArray)" works
"console.log(ResultsArray[1])" returns "undefined"
However, "ResultsArray[1]" works perfectly fine when executed in Firefox console directly.
What am I doing wrong?
<script>
ArrayOfTickers=["SPY","DIA","IWM","C"];
ResultsArray=[];
SomeArray=[]
function Main(Array, callback){
recursive(0);
function recursive(counter) {
if (counter < ArrayOfTickers.length) {
fetch("https://api.iextrading.com/1.0/stock/" + ArrayOfTickers[counter] + "/time-series")
.then(function(response) {
response = response.json()
.then(function(data) {
ResultsArray[counter]=data
})
})
recursive(counter+1);
} else {
callback(ResultsArray);
};
} //End recursive function
}; //End Main Function.
Main(ArrayOfTickers, function(ResultsArray){
console.log(ResultsArray)
})
</script>
"console.log(ResultsArray)[1])"
Should be console.log(ResultsArray[1]), bad parentheses.
First, if you are going to use callbacks, use error-first callbacks like this:
const results = [];
function main(cb) {
(function recursive(counter) {
if (counter >= ArrayOfTickers.length) {
return cb(null);
}
const url = `https://api.iextrading.com/1.0/stock/${ArrayOfTickers[counter]}/time-series`;
fetch(url)
.then(function (response) {
return response.json();
})
.then(function (data) {
results.push(data);
recursive(counter + 1);
})
.catch(cb);
})(0)
}
main(function (err) {
if (err) throw err;
console.log(results)
});
// but it might be better to use promises like so
function main() {
const recursive = function (counter) {
if (counter >= ArrayOfTickers.length) {
return;
}
const url = `https://api.iextrading.com/1.0/stock/${ArrayOfTickers[counter]}/time-series`;
return fetch(url)
.then(function (response) {
return response.json();
})
.then(function (data) {
results.push(data);
return recursive(counter + 1);
});
};
return recursive(0);
}
main().then(function(){
// check results
})
.catch(function(err){
// handle error
});

Recursively calling asynchronous function that returns a promise

I'm trying to recursively call AWS's SNS listEndpointsByPlatformApplication. This returns the first 100 endpoints then a token in NextToken if there are more to return (details: AWS SNS listEndpointsByPlatformApplication).
Here's what I've tried:
var getEndpoints = function(platformARN, token) {
return new models.sequelize.Promise(function(resolve, reject) {
var params = {
PlatformApplicationArn: platformARNDev
};
if (token != null) {
params['NextToken'] = token;
}
sns.listEndpointsByPlatformApplication(params, function(err, data) {
if (err) {
return reject(err);
}
else {
endpoints = endpoints.concat(data.Endpoints); //save to global var
if ('NextToken' in data) {
//call recursively
return getEndpoints(platformARN, data.NextToken);
}
else {
console.log('trying to break out!');
return resolve(true);
}
}
});
});
}
I'm calling it with:
getEndpoints(platformARNDev, null)
.then(function(ret) {
console.log('HERE!');
}, function(err) {
console.log(err);
});
Problem is: the first call happens, then the recursive call happens, and I get the message trying to break out! but the HERE! never gets called. I've got something wrong with how my promises are returning I think.
Grateful for pointers.
The problem is that you try and resolve/reject partially completed query. Here is a complete working example with dummy service. I incapsulated the data grabbing into it's own recursive function and only do resolve/reject when i've completely fetched all the data or stumbled upon an error:
// This is the mock of the service. It yields data and token if
// it has more data to show. Otherwise data and null as a token.
var dummyData = [0, 1, 2, 3, 4];
function dummyAsyncCall(token, callback) {
token = token || 0;
setTimeout(function() {
callback({
dummyDataPart: dummyData[token],
token: (typeof (dummyData[token]) == 'undefined') ? null : (token + 1)
});
});
}
// Here is how you would recursively call it with promises:
function getAllData() {
//data accumulator is sitting within the function so it doesn't pollute the global namespace.
var dataSoFar = [];
function recursiveCall(token, resolve, reject) {
dummyAsyncCall(token, function(data) {
if (data.error) {
reject(data.error);
}
if (!data.token) {
//You don't need to return the resolve/reject result.
resolve(dataSoFar);
} else {
dataSoFar = dataSoFar.concat(data.dummyDataPart);
recursiveCall(data.token, resolve, reject);
}
});
}
return new Promise(function(resolve, reject) {
// Note me passing resolve and reject into the recursive call.
// I like it this way but you can just store them within the closure for
// later use
recursiveCall(null, resolve, reject);
});
}
//Here is the call to the recursive service.
getAllData().then(function(data) {
console.log(data);
});
Fiddle with me
That's because you dont need to return resolve/reject, just call resolve/reject when the recursive call completes. A rough code would look like this
var getEndpoints = function(platformARN, token) {
return new models.sequelize.Promise(function(resolve, reject) {
var params = {
PlatformApplicationArn: platformARNDev
};
if (token != null) {
params['NextToken'] = token;
}
sns.listEndpointsByPlatformApplication(params, function(err, data) {
if (err) {
reject(err);
}
else {
endpoints = endpoints.concat(data.Endpoints); //save to global var
if ('NextToken' in data) {
//call recursively
getEndpoints(platformARN, data.NextToken).then(function () {
resolve(true);
}).catch(function (err) {
reject(err);
});
}
else {
console.log('trying to break out!');
resolve(true);
}
}
});
});
}
(caution: this is just a rough code, may work or may not, but is to give a general idea)
I've added a code snippet below, to support this concept, and it works great, check it out.
i = 0;
$('#output').empty();
function pro() {
return new Promise(function(resolve, reject) {
if (i > 3) {
resolve();
return;
}
window.setTimeout(function() {
console.log(i);
$('#output').append(i).append('<br/>');
i += 1;
pro().then(function() {
resolve()
}).catch(function() {
reject()
});
}, 2000);
});
}
pro().then(function () { $('#output').append("now here"); })
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="output"></div>

Promise.promisify is not a function

I wrote JavaScript like this:
var keys=null;
var promise=Promise.promisify(alchemyapi.keywords("url",myUrl,{},function(response) {
var keywords = { url:myUrl, response:JSON.stringify(response,null,4), results:response['keywords'] };
return keywords;
}));
promise.then(
(result)=>{
var keys=result;
console.log(keys);
},
(error)=>console.log(error)
);
I'm using AlchemyAPI and trying to store data I got into my database
How should I do?
You should be able to use Promise to return expected results by removing .promisify which is not a built-in Promise method ; substituting passing keywords to resolve within Promise constructor for return
var keys = null
, promise = new Promise(function(resolve) {
alchemyapi.keywords("url", myUrl, {}, function(response) {
var keywords = {url: myUrl
, response: JSON.stringify(response,null,4)
, results:response['keywords']
};
resolve(keywords);
// error handling ?
})
}).then(function(result) {
keys = result;
console.log(keys)
}, function(err) {
console.log(err)
})
For a more general Promise.promisify function without Bluebird, I ended up writing this:
function promisify(func) {
return function promiseFunc(options) {
return new Promise(function executor(resolve, reject) {
func(options, function cb(err, val) {
if (err) {
return reject(err);
} else {
return resolve(val);
}
});
});
}
}
Hopefully someone else finds this helpful, but in most cases it's probably worth importing Bluebird.

All functions in Q.all block are not being promised

I have the following code below in a then block
The issue I'm facing is at the end when i do the res.json(optionData1) its not returning the fully completed js data object i.e. the output after the processData function is missing
Am i using Q.all in the correct way?
var processUserInfo = function(categoryToProcess, inputToProcess, optionComingIn) {
var d = Q.defer();
if (optionData1['option'] == optionComingIn) {
if (optionData1[categoryToProcess].hasOwnProperty(inputToProcess)) {
optionData1[categoryToProcess][inputToProcess]++;
} else {
optionData1[categoryToProcess][inputToProcess] = 1;
}
d.resolve(optionData1);
}
}
var processData = function(item, optionComingIn) {
var d = Q.defer();
return User.find(
{_id: item},
{gender: 1, country:1},
function(req, foundUser) {
processUserInfo('gender', foundUser[0]['gender'], optionComingIn)
.then(function(resolve,reject) {
d.resolve();
});
});
return d.promise;
}
Q.all(foundQ[0]['people'].map(function(item) { // Or Q.allSettled
processCounts(item['optionSelected']);
processData(item['userID'], item['optionSelected']);
}))
.then(function(){
res.json(optionData1); //Doesnt give me the full result
});
Thanks
UPDATE: Using the return method as in the answer below got everything working.
Here is code which may work - too much "unknown" in your code snippet to be sure
modified processData to return a promise that resolves when user.Find is done
added a return in the .map, so the promise returned by processData is waited on in Q.all
So ... here's the fixed code (processuserInfo unchanged so omitted form the answer)
var processData = function (item, optionComingIn) {
// return a promise to wait for
return Q.promise(function(resolve, reject) {
User.find({
_id: item
}, {
gender: 1,
country: 1
},
function (req, foundUser) {
processUserInfo('gender', foundUser[0]['gender'], optionComingIn);
resolve();
}
);
});
}
Q.all(foundQ[0]['people'].map(function (item) { // Or Q.allSettled
processCounts(item['optionSelected']);
return processData(item['userID'], item['optionSelected']);
// return added
}))
.then(function () {
res.json(optionData1); //Doesnt give me the full result
});

Nested promise with Parse.com

I've a function to update the DB rows one by one with Parse's promise
exports.update = function (items, successHandler, errorHandler) {
Parse.Cloud.useMasterKey();
var Item = Parse.Object.extend("Item");
Parse.Promise.as().then(function () {
var promises = [];
for (var i = 0; i < items.length; i++) {
(function (j) { //create new closure so i changes with each callback
var query = new Parse.Query(Item);
query.equalTo("sku", items[j]['sku'];);
promises.push(query.find({
success: function (results) {
if (results.length === 1) {
var object = results[0];
console.log('Item exists, now updating..');
return object.save(items[j]).then(function () {
console.log("Item saved"); // never called, why?
}, function (error) {
console.error("Item not saved with error: " + error.message); // never called, why?
});
}
},
error: function (error) {
console.error("Failure during querying..");
}
}));
})(i);
}
return Parse.Promise.when(promises);
}).then(function () {
return successHandler("Item updated.");
}, function (error) {
return errorHandler(error);
}
);
};
The problem is, the object.save is actually called and data is being saved in the DB, however, the following two promises are never called, not matter success or not.
I think because the Promise you return from your inner success function is not going to be necessarily fulfilled. You better promisify the query.find() function with a .then() instead
promises.push(query.find().then(function(results) {
if (results.length === 1) {
var object = results[0];
console.log('Item exists, now updating..');
return object.save(items[j]);
}
}).then(function() {
console.log("Item saved");
}, function (error) {
console.error("Item not saved with error: " + error.message);
});
}));

Categories