Node.js Promises Q.all is not working - javascript

I have this readLines function to parse line by line that called from:
var fs = require('fs');
var Q = require('q');
Q.all(readLines(fs.createReadStream("/tmp/test.txt"), console.log)).then(function () {
console.log('Done');
};
function readLines(input, func) {
var remaining = '';
input.on('data', function (data) {
remaining += data;
var index = remaining.indexOf('\n');
while (index > -1) {
var line = remaining.substring(0, index);
remaining = remaining.substring(index + 1);
func(line);
index = remaining.indexOf('\n');
}
});
input.on('end', function () {
if (remaining.length > 0) {
func(remaining);
}
});
};
Could anyone help why I never got "Done"? Any tutorial to understand how Promises work?

This will work better for you. Please read the comments in the code - it's actually only a few extra lines, and that was including adding error handing.
var fs = require('fs');
var Q = require('q');
// you don't need Q.all unless you are looking for it to resolve multiple promises.
// Since readlines returns a promise (a thenable), you can just then() it.
readLines(fs.createReadStream("./vow.js"), console.log).then(function (x) {
console.log('Done', x);
});
function readLines(input, func) {
// you need to create your own deferred in this case - use Q.defer()
var deferred = Q.defer();
var remaining = '';
input.on('data', function(data) {
remaining += data;
var index = remaining.indexOf('\n');
while (index > -1) {
var line = remaining.substring(0, index);
remaining = remaining.substring(index + 1);
func(line);
index = remaining.indexOf('\n');
}
});
input.on('end', function() {
if (remaining.length > 0) {
func(remaining);
console.log('done');
}
// since you're done, you would resolve the promise, passing back the
// thing that would be passed to the next part of the chain, e.g. .then()
deferred.resolve("Wouldn't you want to return something to 'then' with?");
});
input.on('error', function() {
console.log('bother');
// if you have an error, you reject - passing an error that could be
// be caught by .catch() or .fail()
deferred.reject(new Error('Regrettable news - an error occured.'));
});
// you return the promise from the deferred - not the deferred itself
return deferred.promise;
};

Related

Multiple promise resolve in JQuery

I have this multiple promise request that needs to be resolved before continuing next set of requests.
If the printItemList length is 1, it works fine. But the problem starts hapenning when its more than 2.
The idea is to fire print_label once the previous print_label is resolved successfully. Right now its getting fired immediately and not waiting for previous request to finish.
Is there anything I'm doing wrong here.
Any help on this would be greatly appreciated.
function print_batch(printItemList){
var chain = $.when();
var arr = [];
for(var batch_size = 0; batch_size < printItemList.length; batch_size ++){
(function(i){
chain = chain.then(function() {
$.when.apply($, arr).done(function(){
arr = print_label(printItemList[i]);
});
});
})(batch_size);
}
}
function print_label(selectedRow) {
var d = $.Deferred();
var chain = $.when();
var arr = [];
var request = buildLabel(selectedRow);
var noOfLabel = parseInt(selectedRow.labelCount);
var url = 'API_URL';
var epos = new epson.ePOSPrint(url);
for (var count = 0; count < noOfLabel; count++) {
(function(i){
chain = chain.then(function() {
var def = sendRequest(selectedRow, epos, request);
arr.push(def);
return def;
});
})(count);
}
return arr;
}
function sendRequest(selectedRow, epos, request){
var deferred = $.Deferred();
epos.send(request);
epos.onreceive = function(res) {
return deferred.resolve(res);
}
epos.onerror = function(err) {
return deferred.reject();
}
return deferred.promise();
}
function print_batch(printItemList){
//create recursive method
function print_next_label(index) {
//if we have not reached the end of the list, do more work
if (index < printItemList.length) {
//call the method, wait for all the promises to finish
$.when.apply($, print_label(printItemList[index])).done(function(){
//now that they are done, try to print the next label
print_next_label(++index);
});
}
}
//start the recursive method loop with index 0
print_next_label(0);
}

Node.js Promises within promises not waiting for for loop to return data

The return Promise.all([photoArray]) returns an empty array, seemingly not waiting for the callFB to return its promise that then pushes into the array.
I am not sure what I am doing wrong but am relatively new to Promises with for loops and Ifs.
I am not sure exactly if I am using the correct number of Promises but I seem to not be able to get the 3rd tier Promise.all to wait for the for loop to actually finish (in this scenario, the for loop has to look through many item so this is causing an issue where it is not triggering callFeedback for all the items it should before context.done() gets called.
I have tried using Q.all also for the Promise.all([photoArray]) but have been unable to get that working.
module.exports = function (context, myBlob) {
var res = myBlob
var promiseResolved = checkPhoto(res,context);
var promiseResolved2 = checkVideo(res,context);
Promise.all([promiseResolved, promiseResolved2]).then(function(results){
context.log(results[0], results[1]);
// context.done();
});
});
};
};
function checkPhoto(res, context){
return new Promise((resolve, reject) => {
if (res.photos.length > 0) {
var photoArray = [];
for (var j = 0; j < res.photos.length; j++) {
if (res.photos[j].feedbackId !== null){
var feedbackId = res.photos[j].feedbackId;
var callFB = callFeedback(context, feedbackId);
Promise.all([callFB]).then(function(results){
photoArray.push(results[0]);
});
} else {
photoArray.push("Photo " + j + " has no feedback");
}
}
return Promise.all([photoArray]).then(function(results){
context.log("end results: " + results);
resolve(photoArray);
});
} else {
resolve('No photos');
}
})
}
function checkVideo(res, context){
return new Promise((resolve, reject) => {
same as checkPhoto
})
}
function callFeedback(context, feedbackId) {
return new Promise((resolve, reject) => {
var requestUrl = url.parse( URL );
var requestBody = {
"id": feedbackId
};
// send message to httptrigger to message bot
var body = JSON.stringify( requestBody );
const requestOptions = {
standard
};
var request = https.request(requestOptions, function(res) {
var data ="";
res.on('data', function (chunk) {
data += chunk
// context.log('Data: ' + data)
});
res.on('end', function () {
resolve("callFeedback: " + true);
})
}).on('error', function(error) {
});
request.write(body);
request.end();
})
}
The code suffers from promise construction antipattern. If there's already a promise (Promise.all(...)), there is never a need to create a new one.
Wrong behaviour is caused by that Promise.all(...).then(...) promise isn't chained. Errors aren't handled and photoArray.push(results[0]) causes race conditions because it is evaluated later than Promise.all([photoArray])....
In case things should be processed in parallel:
function checkPhoto(res, context){
if (res.photos.length > 0) {
var photoArray = [];
for (var j = 0; j < res.photos.length; j++) {
if (res.photos[j].feedbackId !== null){
var feedbackId = res.photos[j].feedbackId;
var callFB = callFeedback(context, feedbackId);
// likely no need to wait for callFB result
// and no need for Promise.all
photoArray.push(callFB);
} else {
photoArray.push("Photo " + j + " has no feedback");
}
}
return Promise.all(photoArray); // not [photoArray]
} else {
return 'No photos';
};
}
callFB promises don't depend on each other and thus can safely be resolved concurrently. This allows to process requests faster.
Promise.all serves a good purpose only if it's used to resolve promises in parallel, while the original code tried to resolve the results (results[0]).
In case things should be processed in series the function benefits from async..await:
async function checkPhoto(res, context){
if (res.photos.length > 0) {
var photoArray = [];
for (var j = 0; j < res.photos.length; j++) {
if (res.photos[j].feedbackId !== null){
var feedbackId = res.photos[j].feedbackId;
const callFBResult = await callFeedback(context, feedbackId);
// no need for Promise.all
photoArray.push(callFBResult);
} else {
photoArray.push("Photo " + j + " has no feedback");
}
}
return photoArray; // no need for Promise.all, the array contains results
} else {
return 'No photos';
};
}
Add try..catch to taste.

Returning Then as Undefined. Q in NodeJS

I am trying to return values from my REST request and in the success handler I am returning all the values as a deferred promise but when i run the landingPage function I can't resolve the deferred promise. It gives me .then is undefined. I am using Unirest library for HTTP requests and Q for Promises.
Is it because the return in the request callback returned the data earlier before the server could fetch the data and hence it didn't returned the deferred object or it is something else.
Below is the code:
var landingPage = function (dir) {
Uni.get('https://'+getDirectory(dir).url)
.header('Content-Type','text/html')
.end(function (lp_data) {
var deferred = Q.defer();
if (lp_data.raw_body) {
var $ = cheerio.load(lp_data.raw_body),
cn_europe = $('#location_europe .list-countries li'),
cn_asia = $('#content-location_asia_pacific li'),
cn_north_america = $('#content-location_north_america li');
// Get Europe Company List
var europe = [];
for (var i = 0; i < cn_europe.length; i++) {
var country_name = $(cn_europe[i]).find('a').html().split('(')[0].trim(),
esomar_url = 'https://' + getDirectory(dir).url + '/' + $(cn_europe[i]).find('a').attr('href');
europe.push({country_name: country_name, esomar_url: esomar_url});
}
return europe.length ? deferred.resolve(europe) : deferred.reject(err);
}
});
};
var tests = {
t1: function (dir) {
landingPage(dir)
.then(function (r) {
console.log(r);
})
}
}
Your main function landingPage is not returning a value. The code inside it produces a promise, but you should also return it.
As you want to return the promise held by deferred, you should make deferred a variable that is defined in the outer function scope, so you can return the promise defined by it (deferred.promise):
var landingPage = function (dir) {
var deferred = Q.defer(); // *** define here
Uni.get('https://'+getDirectory(dir).url)
.header('Content-Type','text/html')
.end(function (lp_data) {
var europe = []; // *** define here, before the `if`
if (lp_data.raw_body) {
var $ = cheerio.load(lp_data.raw_body),
cn_europe = $('#location_europe .list-countries li'),
cn_asia = $('#content-location_asia_pacific li'),
cn_north_america = $('#content-location_north_america li');
// Get Europe Company List
for (var i = 0; i < cn_europe.length; i++) {
var country_name = $(cn_europe[i]).find('a').html().split('(')[0].trim(),
esomar_url = 'https://' + getDirectory(dir).url + '/' + $(cn_europe[i]).find('a').attr('href');
europe.push({country_name: country_name, esomar_url: esomar_url});
}
}
// *** Move this out of the above `if`, so you always resolve/reject:
return europe.length ? deferred.resolve(europe) : deferred.reject(err);
});
// *** return the promise (it will not be resolved yet, but that is the idea):
return deferred.promise;
}
However, there is a request to support promises in Unirest (see here), which allows you to do this:
var landingPage = function (dir) {
// return the promise that will now come from this chain:
return Uni.get('https://'+getDirectory(dir).url)
.header('Content-Type','text/html')
.end()
.exec() // turn request object to a promise
.then(function (lp_data) {
var europe = []; // *** define here, before the `if`
if (lp_data.raw_body) {
var $ = cheerio.load(lp_data.raw_body),
cn_europe = $('#location_europe .list-countries li'),
cn_asia = $('#content-location_asia_pacific li'),
cn_north_america = $('#content-location_north_america li');
// Get Europe Company List
for (var i = 0; i < cn_europe.length; i++) {
var country_name = $(cn_europe[i]).find('a').html().split('(')[0].trim(),
esomar_url = 'https://' + getDirectory(dir).url + '/' + $(cn_europe[i]).find('a').attr('href');
europe.push({country_name: country_name, esomar_url: esomar_url});
}
}
// *** Return the data. Throw an error if to be rejected
if (!europe.length) throw "no data!!";
return europe;
});
}
This way you do not have to do the conversion to a promise yourself with a deferred object.

Node.js Function Flow

When I get a request, I want it to generate a 4-character code, then check if it already exists in the database. If it does, then generate a new code. If not, add it and move on. This is what I have so far:
var code = "";
var codeFree = false;
while (! codeFree) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log(rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
client.execute("select * from codes where code=" + code, function(err, result) {
if (! err) {
if (result.rows.length > 0) {
codeFree = false;
} else {
codeFree = true;
}
} else {
console.log('DB ERR: %s', err);
}
console.log(codeFree);
});
console.log('here');
}
This does not do nearly what I want it to do. How can I handle something like this?
You are doing an async task.
When you have an asyncronous task inside your procedure, you need to have a callback function which is going to be called with the desired value as its argument.
When you found the free code, you call the function and passing the code as its argument, otherwise, you call the getFreeCode function again and passing the same callback to it. Although you might consider cases when an error happens. If your the db call fails, your callback would never get called. It is better to use a throw/catch mechanism or passing another argument for error to your callback.
You can achieve what you need to do by doing it this way:
function getFreeCode(callback) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log(rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
client.execute("select * from codes where code="+code, function(err, result) {
if(!err) {
if(result.rows.length > 0) {
getFreeCode(callback);
} else {
callback(code);
}
}else {
console.log('DB ERR: %s', err);
}
console.log(codeFree);
});
console.log('here');
}
// in your main:
getFreeCode(function (code) {
console.log(' this code was free: ' + code)
})
I recommend you look into two alternatives to help deal with asynchronous code.
node generator functions using the 'yield' keyword
promises
Using generators requires running a recent version of node with the --harmony flag. The reason I recommend generators is because you can write code that flows the way you expect.
var x = yield asyncFunction();
console.log('x = ' + x);
The previous code will get the value of x before logging x.
Without yielding the console.log would write out x before the async function was finished getting the value for x.
Your code could look like this with generators:
var client = {
execute: function (query) {
var timesRan = 0;
var result = [];
return function () {
return setTimeout(function () {
result = ++timesRan < 4 ? ['length_will_be_1'] : [];
return result;
},1);
};
}
};
function* checkCode () {
var code;
var codeFree = false;
while(!codeFree) {
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log(rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
try {
var result = yield client.execute("select * from codes where code="+code);
codeFree = result.rows.length > 0 ? false : true;
}catch(e) {
console.log('DB ERR: %s', err);
} finally {
console.log(codeFree);
}
console.log('here');
}
}
checkCode().next();
You would leave off the client object. I only added that to make a working example that fakes an async call.
If you have to use an older version of node or do not like the yield syntax then promises could be a worthy option.
There are many promise libraries. The reason I recommend promises is that you can write code that flows the way you expect:
asyncGetX()
.then(function (x) {
console.log('x: ' + x);
});
The previous code will get the value of x before logging x.
It also lets you chain async functions and runs them in order:
asyncFunction1()
.then(function (result) {
return asyncFunction2(result)
})
.then(function (x) { /* <-- x is the return value from asyncFunction2 which used the result value of asyncFunction1 */
console.log('x: ' + x);
});
Your code could look like this with the 'q' promise library:
var Q = require('q');
var client = {
timesRan: 0,
execute: function (query, callback) {
var self = this;
var result = {};
setTimeout(function () {
console.log('self.timesRan: ' + self.timesRan);
result.rows = ++self.timesRan < 4 ? ['length = 1'] : [];
callback(null, result);
},1);
}
};
function checkCode () {
var deferred = Q.defer();
var codeFree = false;
var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var code = "";
for (var i = 0; i < 4; i++) {
var rand = Math.floor(Math.random() * chars.length);
console.log('rand: %s', rand);
code += chars.charAt(rand);
}
console.log("Code: %s generated.", code);
client.execute("select * from codes where code="+code, function(err, result) {
console.log('err: '+err+', result: ' + JSON.stringify(result));
console.log('result.rows.length: ' + result.rows.length);
if(!err) {
if(result.rows.length > 0) {
codeFree = false;
console.log('result.rows: %s, codeFree: %s', result.rows, codeFree);
checkCode();
} else {
codeFree = true;
console.log('line 36: codeFree: ' + codeFree);
deferred.resolve(code);
}
}else {
console.log('DB ERR: %s', err);
deferred.reject(err);
}
console.log(codeFree);
});
console.log('waiting for promise');
return deferred.promise;
}
checkCode()
.then(function (code) {
console.log('success with code: ' + code);
})
.fail(function(err) {
console.log('failure, err: ' + err);
});
Also omit the client object here. I only added that to make a working example that fakes an async call.
Promises and generators definitely take some time to get used to. It's worth it because they make the code a lot easier to follow in the end than code written with nested callbacks.

How to handle each response in successCallBack of promises in case of dynamic passage of promises to .when()

Here are the cloud functions namely 'batchReq1' and batchPromises.
In any case, if I know the exact number of promises pushed (Consider the size of results to be '2' in function batchPromises(results)) and executed through when(), I can handle the success response by passing that number of result parameters (In the below example request1, request2) in successCallBack of .then().
If I have to process the number of promises pushed to .when() dynamically, then, how can we handle this in SuccessCallBack? Unlike earlier scenario, we can't expect fixed number of results in the then method (batchPromises(results).then(function (result1, result2) {....)
batchReq1
Parse.Cloud.define("batchReq1", function (request, response) {
var results = request.params.imageArray;
batchPromises(results).then(function (result1, result2) {
console.log("Final Result:: Inside Success");
console.log("Final Result:: Inside Success result 1::::"+result1);
console.log("Final Result:: Inside Success result 2::::"+result2);
response.success();
}
// batchPromises(results).then(function (arraySuccess) {
//
// console.log("Final Result:: Inside Success");
// console.log("Final Result:: Inside Success:: Length:: "+arraySuccess.length);
// //Fetch all responses from promises and display
// var _ = require('underscore.js');
// _.each(arraySuccess, function (result) {
//
// console.log("Final Result:: " + result)
//
// });
//
//
// response.success();
//
// }
, function (error) {
console.log("Final Result:: Inside Error");
response.error(error);
});
});
batchPromises
function batchPromises(results) {
var promise = Parse.Promise.as();
var promises = [];
var increment = 0;
var isFromParallelExecution = false;
var _ = require('underscore.js');
_.each(results, function (result) {
var tempPromise = Parse.Promise.as("Promise Resolved ");
promises.push(tempPromise);
}
promise = promise.then(function () {
return Parse.Promise.when(promises);
});
}
increment++;
});
return promise;
}
this is how i handle this...
Parse.Cloud.define("xxx", function(request, response)
{
var channels = ["channel1", "channel2", "channel3", "channel4", "channel5"];
var queries = new Array();
for (var i = 0; i < channels.length; i++)
{
var query = new Parse.Query(channels[i]);
queries.push(query.find());
}
Parse.Promise.when(queries).then(function()
{
var msg = "";
for (var j = 0; j < arguments.length; j++)
{
for (var k = 0; k < arguments[j].length; k++)
{
var object = arguments[j][k];
msg = msg + " " + object.get('somefield');
}
}
response.success(msg);
});
});
Either you just use the arguments object to loop over the results, or you build your arraySuccess without when - it doesn't make much sense here anyway as you batch the requests (executing them sequentially), instead of executing them in parallel:
function batchPromises(tasks) {
var _ = require('underscore.js');
_.reduce(tasks, function (promise, task) {
return promise.then(function(resultArr) {
var tempPromise = Parse.Promise.as("Promise Resolved for "+taks);
return tempPromise.then(function(taskResult) {
resultArr.push(taskResult);
return resultArr;
});
});
}, Parse.Promise.as([]));
}
If you actually wanted to execute them in parallel, use a simple
function batchPromises(tasks) {
var _ = require('underscore.js');
return Parse.Promise.when(_.map(tasks, function (task) {
return Parse.Promise.as("Promise Resolved for "+taks);
}).then(function() {
return [].slice.call(arguments);
});
}

Categories