Multiple user inputs using Nodejs - javascript

I'm trying to write a script that asks three questions in a row, while waiting for user input in between each question.
It seems that I have difficulty understanding how to do this with the non-blocking nature of node.
Here's the code I'm running:
var shell = require('shelljs/global'),
utils = require('./utils'),
readline = require('readline'),
fs = require('fs'),
path = require('path');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
setUserDefaultSettings();
function setUserDefaultSettings() {
var defaultSettingsFile = find('DefaultSettings.json');
var defaultSettingsJSON = [
{
macro: '__new_project_path__',
question: 'Please enter the destination path of your new project: '
},
{
macro: '__as_classes_path__',
question: 'Please enter the path to your ActionScript Classes: ',
},
{
macro: '__default_browser_path__',
question: 'Please enter the path to your default browser: '
}
];
var settingsKeys = [];
var index = 0;
if (!test('-f', 'UserSettings.json')) {
cp(defaultSettingsFile, 'UserSettings.json');
}
var userSettingsFile = pwd() + path.sep + find('UserSettings.json');
fs.readFile(userSettingsFile, 'utf8', function (err, data) {
if (err) {
echo('Error: ' + err);
return;
}
data = JSON.parse(data);
for(var attributename in data) {
settingsKeys.push(attributename);
}
defaultSettingsJSON.forEach(function(key) {
index++;
// Check if macros have been replaced
if (data[settingsKeys[index - 1]] === key.macro) {
// Replace macros with user input.
replaceSettingMacro(userSettingsFile, key.macro, key.question);
}
});
});
}
function replaceSettingMacro(jsonFile, strFind, question) {
askUserInput(question, function(strReplace) {
sed('-i', strFind, strReplace, jsonFile);
});
}
function askUserInput(string, callback) {
rl.question(string, function(answer) {
fs.exists(answer, function(exists) {
if (exists === false) {
echo('File ' + answer + ' not found!');
askUserInput(string, callback);
} else {
callback(answer);
}
});
});
}
Only the first question is asked, as the script continues execution while the user is inputting their answer. I understand why this is the case, but don't know how I can work around this.

This looks like an appropriate place to implement a queue. The queue will initiate tasks one at a time only starting the next after the previous has finished.
in your askUserInput method try something like:
var questionQueue = [];
function askUserInput(string, callback) {
if(questionQueue.length == 0) {
rl.question(string, function(answer) {
fs.exists(answer, function(exists) {
if (exists === false) {
echo('File ' + answer + ' not found!');
askUserInput(string, callback);
} else {
callback(answer);
if(questionQueue.length > 0){
var question questionQueue.shift();
askUserInput(question.string, question.callback);
}
}
});
});
}
else {
questionQueue.push({string: string, callback: callback});
}
}
Another option included extending your callback further up the call stack and invoke the next question when you receive the callback from the previous.

The 2 ways I'd handle this:
a) use sync-prompt - would make it very easy
If you still wanted to handle it asynchronously,
b) I'd use a promise library such as When and do a When.all([array of promises]) before continuing.

Below is the correct solution to my question, following #Preston-S advice:
var questionList = [];
var macroList = [];
setUserDefaultSettings();
function setUserDefaultSettings() {
var defaultSettingsFile = find('DefaultSettings.json');
var defaultSettingsJSON = [
{
macro: '__new_project_path__',
question: 'Please enter the destination path of your new project: '
},
{
macro: '__as_classes_path__',
question: 'Please enter the path to your ActionScript Classes: ',
},
{
macro: '__default_browser_path__',
question: 'Please enter the path to your default browser: '
}
];
if (!test('-f', 'UserSettings.json')) {
cp(defaultSettingsFile, 'UserSettings.json');
}
userSettingsFile = pwd() + path.sep + find('UserSettings.json');
var settingsKeys = [];
var index = 0;
var canAskQuestion = false;
fs.readFile(userSettingsFile, 'utf8', function (err, data) {
if (err) {
echo('Error: ' + err);
return;
}
data = JSON.parse(data);
for(var attributename in data) {
settingsKeys.push(attributename);
}
defaultSettingsJSON.forEach(function(key) {
index++;
// Check if macros have been replaced
if (data[settingsKeys[index - 1]] === key.macro) {
// Replace macros with user input.
questionList.push(key.question);
macroList.push(key.macro);
if (!canAskQuestion) {
askQuestion(function() {
copyTemplate();
});
canAskQuestion = true;
}
} else {
copyTemplate();
}
});
});
}
function askQuestion(callback) {
replaceSettingMacro(userSettingsFile, macroList.shift(), questionList.shift(), function() {
if (macroList.length < 1 || questionList.length < 1) {
callback();
} else {
askQuestion(callback);
}
});
}
function replaceSettingMacro(jsonFile, strFind, question, callback) {
askUserInput(question, function(strReplace) {
sed('-i', strFind, strReplace, jsonFile);
callback();
});
}
function askUserInput(string, callback) {
rl.question(string, function(answer) {
fs.exists(answer, function(exists) {
if (exists === false) {
echo('File ' + answer + ' not found!');
askUserInput(string, callback);
} else {
callback(answer);
}
});
});
}
function copyTemplate() {
rl.close();
}

Related

ReplaceAll causing issues in array.reduce

I am still pretty new to this, so forgive me if I dont' say this correctly. We have an array.reduce that calls a method with a returning promise that iterates through a list of files and post results to the db. Everything was working great, until it ran into a field that had an apostrophe in it and then the db insert fails. This is the field value. 'Expected 100002822' to be 100002822.'
I tried adding a replaceAll on the field and now get an error in the array.reduce.
Here is the .reduce
console.log('Found test results in ' + matches.length + ' files. Parsing and posting to the database now...');
var startTime = moment();
var parser = new Parser();
matches.reduce(function (p, val) {
return p.then(function () {
return parser.parseResults(val);
});
}, Promise.resolve()).then(function (finalResult) {
var endTime = moment();
var testDuration = moment.duration(endTime.diff(startTime));
console.log(chalk.blue('*** File parsing time: ' + testDuration.humanize() + ' ***'));
if (finalResult.insertSuccess == matches.length) {
var publishOut = {
totalFiles: matches.length,
totalTests: 0,
totalTestsSuccess: 0,
totalTestsFailed: 0
}
publishOut.totalTests += finalResult.totalTests;
publishOut.totalTestsSuccess += finalResult.testPassedCount;
publishOut.totalTestsFailed += finalResult.testFailedCount;
console.log(`Successfully inserted ${finalResult.insertSuccess} of ${publishOut.totalTests} test results.`);
// for (var i = 0; i < matches.length; i++) {
// var currentFile = `./testing/results/${matches[i]}`;
// fs.unlinkSync(currentFile);
// }
resolve(publishOut);
} else {
reject('Only ' + finalResult.insertSuccess + ' of ' + matches.length + ' successfully posted to the database');
}
}, function (err) {
reject('error in reduce', err);
});
I have tried several different ways of using the replaceAll with the same failure. It hits this code from the array.reduce
}, function (err) {
reject('error in reduce', err);
});
And this is the called method. The added code causing the failure in the .reduce is this Message = expectation.message.replaceAll("'", "");
protractorParser.prototype.parseResults = function (fileName) {
return new Promise((resolve, reject) => {
//console.log('In parseresults', fileName);
var currentFile = './testing/results/' + fileName
json.readFile(currentFile, function (err, obj) {
if (err != null) {
console.log('error reading file', err);
reject(err);
} else {
resolve(obj);
}
});
}).then(function (obj) {
var results = [];
for (var suite in obj) {
var specs = obj[suite].specs;
for (let i = 0; i < specs.length; i++) {
const assert = specs[i];
const tcR = /TC[\d]+/;
const tc = assert.description.match(tcR);
let Passed = 1;
let Message = '';
let Stack = '';
testResults.totalTests++;
if (assert.failedExpectations.length) {
const expectation = assert.failedExpectations[assert.failedExpectations.length - 1];
Passed = 0;
Message = expectation.message.replaceAll("'", "");
Stack = expectation.stack.split('\n')[1].trim();
testResults.testFailedCount++
} else {
testResults.testPassedCount++
}
if (tc != null) {
const time = moment().utcOffset(config.get('settings.timeOffset')).format('YYYY-MM-DDTHH:mm:ss');
const promise = utility.TestDataManager.insertAutomationResults(tc[0], assert.description, Passed, process.env.testBuild, 'P', Message, Stack, 0, time, '');
results.push(promise.then(() => {
//fs.unlinkSync(currentFile);
testResults.insertSuccess++;
//console.log('insertSuccess', testResults.insertSuccess);
},
err => { console.log('… failed', err); throw err; }
));
} else {
console.log('no test case found for test: ' + assert.description + ' -- skipping');
// I don't think you want to `throw err` here, right?
}
}
}
return Promise.all(results).then(() => testResults);
});
};

Foreach with Promise not waiting on method results

I am trying to iterate through the JSON files generated by the protractor tests. I pull all the file names into an array and call a method that opens and parses through the each file, post the results to the database and pass back a passed/failed flag.
I have tried all the examples here
Make angular.forEach wait for promise after going to next object and still get the same results.
The method is actually called, but the results are not posted to the db. I have tested the parser.parseResults on an individual file and it successfully posted to the db, so it has to have something to do with the promise not resolving correctly.
Is it not possible to do something like this in the jasmine/protractor framework? Or do I have something wrong in the code?
I have included the code for my latest attempt.
Thank You
Christine
matches.reduce(function (p, val) {
console.log('val', val);
return p.then(function () {
return parser.parseResults(val);
});
}, Promise.resolve()).then(function (finalResult) {
console.log('finalResult = ', finalResult);
}, function (err) {
console.log('error in reduce',err);
});
parser.parseResults code
protractorParser.prototype.parseResults = function (fileName) {
return new Promise((resolve, reject) => {
console.log('In parseresults', fileName);
json.readFile(fileName, function (err, obj) {
try {
if (err != null) {
console.log('error reading file',err);
reject(err);
}
console.log('obj - ',obj);
var results = [];
var Passed = 0;
var Message = '';
var Stack = '';
for (var suite in obj) {
var specs = obj[suite].specs;
console.log('spec - ', specs);
if (specs.length > 0) {
for (var i = 0; i < specs.length; i++) {
var assert = specs[i];
var tcR = new RegExp(/TC[\d]+/);
var tc = assert.description.match(tcR);
if (!assert.failedExpectations.length) {
Passed = 1;
}
else {
assert.failedExpectations.forEach((expectation) => {
Message = expectation.message;
Stack = expectation.stack.split('\n')[1].trim();
})
Passed = 0;
}
if (tc != null) {
utility.TestDataManager.insertAutomationResults(tc[0], assert.description, Passed, process.env.testBuild,
'P', Message, Stack, 0, moment().utcOffset(config.get('settings.timeOffset')).format('YYYY-MM-DDTHH:mm:ss'), '')
.then(function (resp) {
resolve(Passed);
}, (err) => {
console.log('Posting to Database failed ', err);
reject(err);
});
} else {
console.log('no test case found for test: ' + assert.description + ' -- skipping');
reject(err);
}
}
}
}
}
catch (err) {
console.log('rejecting opening file');
reject(err);
}
});
})
}
If there is not exactly one suite in the obj, with exactly one spec, then your promise is either resolved not at all or multiple times.
Avoid wrapping too many things in the new Promise constructor - always promisify on the smallest possible level, and use promise chaining afterwards.
protractorParser.prototype.parseResults = function (fileName) {
return new Promise((resolve, reject) => {
console.log('In parseresults', fileName);
json.readFile(fileName, function (err, obj) {
if (err != null) {
console.log('error reading file', err);
reject(err);
} else {
resolve(obj);
}
});
}).then(function(obj) {
console.log('obj - ',obj);
var results = [];
for (var suite in obj) {
var specs = obj[suite].specs;
console.log('spec - ', specs);
for (let i = 0; i < specs.length; i++) {
const assert = specs[i];
const tcR = /TC[\d]+/;
const tc = assert.description.match(tcR);
let Passed = 1;
let Message = '';
let Stack = '';
if (assert.failedExpectations.length) {
const expectation = assert.failedExpectations[assert.failedExpectations.length-1];
Passed = 0;
Message = expectation.message;
Stack = expectation.stack.split('\n')[1].trim();
}
if (tc != null) {
const time = moment().utcOffset(config.get('settings.timeOffset')).format('YYYY-MM-DDTHH:mm:ss');
const promise = utility.TestDataManager.insertAutomationResults(tc[0], assert.description, Passed, process.env.testBuild, 'P', Message, Stack, 0, time, '');
results.push(promise.catch(err => {
console.log('Posting to Database failed ', err);
throw err;
}));
} else {
console.log('no test case found for test: ' + assert.description + ' -- skipping');
// I don't think you want to `throw err` here, right?
}
}
}
return Promise.all(results);
});
};

Node.js call async function from within do while loop

I want to call an async function from within a do while loop and exit only if the function doesn't return a value.
Is there a way to manage this without having to actually wait outside the callback?
Here's the code that obviously always exits in the first loop since the function hasn't called back yet, when the while () is evaluated:
var i = 0;
do {
var foundListing;
if (i) {
listing.slug = listing.slug + '-' + (i + 1);
}
listingService.getListingBySlug(listing, function(error, pFoundListing) {
if (error) {
return callback(util.errorParser(error));
} else if (Object.keys(pFoundListing).length) {
foundListing = pFoundListing;
i++;
}
});
//Do I have to wait here???
} while (foundListing && Object.keys(foundListing).length);
For Clarification:
The point here is to generate a unique slug. If the slug already exists, i append a number and check again if it exists. When I get to the number that doesn't exist yet, I'm done.
Update:
I found a way using recursion. I posted the working snippet as an answer.
I don't have the possiblity to test it, but maybe a recursive function should do:
const listingService = {
getListingBySlug(listing, callback) {
setTimeout(() => {
if (listing.slug === 'listing-5') {
callback(
false,
{
name: 'Listing 1',
slug: 'listing-1',
}
);
}
else {
callback(false, {});
}
}, 1000);
},
};
function checkListingExists(slug) {
return new Promise((resolve, reject) => {
const listing = { slug };
listingService.getListingBySlug(listing, (error, pFoundListing) => {
if (error) {
reject(error);
} else if (Object.keys(pFoundListing).length) {
resolve(pFoundListing);
} else {
resolve(false);
}
});
});
}
function nextListing(prefix, index) {
checkListingExists(prefix + '-' + index)
.then((listing) => {
if (listing === false) {
nextListing(prefix, index + 1);
} else {
console.log(listing);
}
})
.catch(() => {
// deal with error response
});
}
nextListing('listing', 0);
You should use promise in such cases. You can do something like this.
var DEFERRED = require('deferred');
var deferred = new DEFERRED();
var i =0;
while(1) {
listingService.getListingBySlug(listing, function(error, pFoundListing) {
if (error) {
return callback(util.errorParser(error));
deferred.reject();
break;
} else if (Object.keys(pFoundListing).length) {
i++;
listing.slug = listing.slug + '-' + (i + 1);
if(pFoundListing) {deferred.resolve()}
break;
}
});
deferred.promise.then(function() {
// You will have the listing.slug value here.
});
Btw you should not use Object.keys() to determine whether the object is empty or not. Simply create your own isEmpty method somewhere in utils file and check for the properties. If your pFoundListing is very large it will have the severe performance issue. For small objects (array) you would not notice the difference.
I made it work using recursion as suggested by Giacomo Cosimato.
Here's the snippet that worked:
listingController.js
saveListing: function(listing, callback) {
listingService.findOpenSlug(listing, listing.slug, 1, function(error, listing) {
if (error) {
return callback(util.errorParser(error));
} else {
listingService.saveListing(listing, function(error, savedListing) {
//do some work
});
}
});
}
listingService.js
var classMethods = {
findOpenSlug: function(listing, baseSlug, i, callback) {
listingRepository.getListingBySlug(listing.slug, function(error, foundListing) {
if (error) {
return callback(error);
} else if (Object.keys(foundListing).length) {
listing.slug = baseSlug + '-' + (i + 1)
i++;
classMethods.findOpenSlug(listing, baseSlug, i, callback);
} else {
callback(null, listing);
}
});
},
[...]
}

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 set object array back to iterated collection from nested find() in MongoDB?

I develop application using AngularJS , NodeJS and MongoDB. I'd like to load Product with classified by ProductCategoryCode sending from
AngualrJS to NodeJS. First, I need to find Products by ProductCategoryCode and then iterate for each product to find Uoms by UomCode and ContainUomCode
which each product should has 2 uoms. How can I set uom object docUom back to product document doc[i] and update to product collection doc?
For following code line
doc[i].Uom = docUom;
The system throw error cannot set property 'Uom' of undefined.
Here is product.js snippet code.
router.get("/LoadProductByProductCategoryCode/:productCategoryCode", function (req, res) {
console.log('user.js -> /users ');
var productCategoryCode = req.params.productCategoryCode;
console.log(productCategoryCode );
var MongoClient = require('mongodb').MongoClient,
format = require('util').format;
MongoClient.connect('mongodb://localhost:27017/NodeDB', function (err, db) {
if (err) throw err;
var query = { ProductCategoryCode : productCategoryCode}
var new_product = [];
findProduct(db, query, function (err, doc) {
if(err) {
// something went wrong
console.log(err);
return;
}
if (doc) {
console.log("Found Product..."+doc.length);
  for (var i = 0; i < doc.length; i++) {
console.log(doc[i].ProductCode + " each document " + doc[i].UomCode + " " + doc[i].ContainUomCode);
var qUom = {
$or: [ { UomCode: doc[i].UomCode}, { UomCode: doc[i].ContainUomCode } ]
}
// Find uom
findUom(db, qUom, function(errUom, docUom) {
if(errUom) {
console.log("error " + errUom);
return;
}
if (docUom) {
doc[i].Uom = docUom;
console.dir(product);
}
});
}
res.json(doc);
} else {
console.log('something happen');
}
}); //End
}); // MongoClient
var findProduct = function (db, query, callback) {
db.collection('Product').find(query).toArray(function (err, doc) {
if(err) {
callback(err);
}
else {
callback(null, doc);
}
});
}
var findUom = function(db, queryUom, callback) {
db.collection('Uom').find(queryUom).toArray(function (err, doc) {
// db.close();
if(err) {
callback(err);
}
else {
callback(null, doc);
}
});
}
});
Any idea? THANKS
Because of the asynchronous nature of the Node.js MongoDB driver, both the findProduct() and findUom() methods start, but don't necessarily complete by the time you reach res.json(doc) meaning doc will still be empty. You are expecting this to work in a linear fashion, but node works differently.
Instead, you should send your response back once all asynchronous calls complete meaning you could try something like:
findProduct(db, query, function (err, doc) {
if(err) {
// something went wrong
console.log(err);
return;
}
var processedProduct = function (item) {
console.log(item.ProductCode + " each document " + item.UomCode + " " + item.ContainUomCode);
var qUom = {
$or: [ { UomCode: item.UomCode}, { UomCode: item.ContainUomCode } ]
}
// Find uom
findUom(db, qUom, function(errUom, docUom) {
if(errUom) {
console.log("error " + errUom);
return;
}
if (docUom) {
item.Uom = docUom;
console.dir(product);
return item;
}
});
}  
if (doc) {
var productsToFind = doc.length;
var products = [];
console.log("Found Products..." + productsToFind);
for (var i = 0; i < doc.length; i++) {
product = doc[i];
product = processedProduct(product);
products.push(product);
productsToFind -= 1;
if(productsToFind === 0){
res.json(products);
}
}
} else {
console.log('something happen');
}
}); //End
I could explain better about asynchronous calls and callbacks as this topic is a bit broad but from the above you can get the idea that I have used a counter productsToFind for all of the inner async calls that once each findUom() call completes this counter decrements and once it reaches 0 it means that all callbacks have fired.

Categories