Node js promise inside for loop always prints Promise { <pending> } - javascript

I'm trying to resolve a promise inside a for-loop in node js. In my code, I have a for loop in which I call a function findincollection that returns a promise. Then I push the data to finalresult array and resolve it once the for loop completes. But the issue I'm facing is it doesn't resolve the complete data. The for loop execution is getting finished before all the promises are resolved.
The line console.log(p1); always prints Promise { } but it eventually gets resolved as you can see in my code in the p1.then() statement, I do get the data one by one. But the finalresult array resolves too early. I also want to know why I always get Promise { } even when the promises are still getting resolved eventually.
Please have a look at my code below :
var mob = [123, 456, 789];
var list = [1, 2, 3, 4, 5, 6, 7, 8];
var res = [];
var finalresult = [];
for (y = 0; y < list.length; y++) {
const p1 = findincollection(list[y], mob, savetofile);
console.log(p1); //always prints Promise { <pending> } 8 times
p1.then(function(dt) {
finalresult.push(dt); //pushes all 3 objects one by one
console.log(dt); //prints 3 objects one by one
client.close();
if (y == (collist.length)) { //check if the loop has reached the last index
resolve(finalresult); //resolves the finalresult array with 1 object only instead of 3. I want this part to resolve the complete finalresult array i.e with all 3 objects.
}
});
}
const findincollection = function(index, mob, save) {
return new Promise((resolve, reject) => {
MongoClient.connect(url, function(err, client) {
assert.equal(null, err);
const db = client.db(dbName);
const collection = db.collection(col);
collection.find({ 'Numbers': { $in: mob } }).toArray(function(err, docs) {
const c = save(index, docs);
c.then(function(m) {
console.log(m); //print's Saved 3 times as the mob array length is 3
client.close();
return resolve(res);
})
});
});
});
}
const save = function(index, data) {
return new Promise((resolve, reject) => {
if (data.length > 0) {
for (var k = 0; k < data.length; k++) {
res.push(data[k]);
}
fs.appendFile('textlogs/' + index + '.txt', data, function(err) {
if (err) throw err;
resolve('Saved');
});
}
});
}
I'm not able to figure out how to make the loop wait until all the promises are resolved or make the code synchronous and then only resolve the finalresult array? How do I do it?

What you need here is Promise.all(). It returns a new promise, which gets resolved once all passed promises get resolved.
You can try something similar to:
var promises = [];
for (y = 0; y < list.length; y++) {
promises.push(findincollection(list[y], mob, savetofile));
}
Promise.all(promises)
.then(dt => {
finalresult.push(dt); //pushes all 3 objects one by one
console.log(dt); //prints 3 objects one by one
client.close();
if (y == (collist.length)) {
resolve(finalresult);
}
}); // <-- this will be raised once all your promises are resolved
I see you're closing client inside each promise and this client is not a local variable. So I think you should be closing it after all your promises are completed.

Related

Promise ReaddirSync not returning values

I have files in the directories, the let resolvers = { ...resolversArray[0], ...resolversArray[1] }; line does get set, however I'm trying to make it so I don't manually need to put in each item of the array.
Here is my code
let resolversArray = [];
let promise = new Promise((resolve, reject) => {
fs.readdirSync(`${__dirname}/modules`).forEach((folder) => {
let temp = require(`./modules/${folder}/resolver.js`);
resolversArray[folder] = temp;
});
if (resolversArray.length > 0) {
resolve(resolversArray);
} else {
reject("Resolvers Array is empty");
}
});
promise.then((array) => {
console.log("returned array", resolversArray);
});
Result is I get a rejected promise with Resolvers Array is empty being returned.

There are 100 promises in an array and we need to process 5 at a time in JS. how to achieve this?

There are 100 promises in an array and we need to process 5 at a time in JS. how to achieve this?
(Asked in Microsoft interview)
Use a pool. There are a number of implementations in JS, such as this one that has a nice looking API:
const PromisePool = require("async-promise-pool");
// concurrency is the only option for PromisePool and enables you to
// choose how many promises will run at once
const pool = new PromisePool({ concurrency: 3 });
// elsewhere add functions to the pool that produce promises. We use
// functions here to prevent the promises from immediately executing.
pool.add(() => thingThatReturnsAPromise());
// you can await pool.all to ensure that all promises in the pool are
// resolved before continuing.
await pool.all();
I would use a function to execute promises in sequence instead of parallel. Once done, create an array of groups of 5 to solve in parallel using Promise.all:
const PROMISES_AMOUNT = 100
const GROUP_AMOUNT = 5
// Function to divide the array in various chuncks of similar size
function chunkArray(myArray, chunk_size){
let index = 0;
let arrayLength = myArray.length;
let tempArray = [];
for (index = 0; index < arrayLength; index += chunk_size) {
myChunk = myArray.slice(index, index+chunk_size);
// Do something if you want with the group
tempArray.push(myChunk);
}
return tempArray;
}
// the promise we will use
function interval(index) {
return new Promise(function(resolve, reject) {
const time = index*100
setTimeout(function() {
console.log(`Waited ${time}!`)
resolve(index);
}, time)
})
};
// Our array of 100 promises
const promises = new Array(PROMISES_AMOUNT).fill(null).map((_, index) => interval(index ))
// The array of 100 promises divided by groups of 5 elements
const groupedPromises = chunkArray(promises, GROUP_AMOUNT).map((promisesGroup) => () => Promise.all(promisesGroup))
// A function to divide an array
function chunkArray(myArray, chunk_size){
var index = 0;
var arrayLength = myArray.length;
var tempArray = [];
for (index = 0; index < arrayLength; index += chunk_size) {
myChunk = myArray.slice(index, index+chunk_size);
// Do something if you want with the group
tempArray.push(myChunk);
}
return tempArray;
}
// A function to execute promises in sequence
const promisesInSequence = (arrayOfTasks) => {
let results = []
return new Promise((resolve, reject) => {
const resolveNext = (arrayOfTasks) => {
// If all tasks are already resolved, return the final array of results
if (arrayOfTasks.length === 0) return resolve(results)
// Extract first promise and solve it
const first = arrayOfTasks.shift()
first().then((res) => {
console.log('Solved a group in parallel: ', res)
results.push(res)
resolveNext(arrayOfTasks)
}).catch((err) => {
reject(err)
})
}
resolveNext(arrayOfTasks)
})
}
promisesInSequence(groupedPromises)
.then((result) => console.log(result))

NodeJS: Parsing data sequentially and asynchronously

I am writing a NodeJS app that will load data from a database, parse it, and then save the parsed result to a different table in the database. Here is what I currently have:
parse(index, from, to) {
var collection = this.getCollectionName();
var interval = global.Settings.Parser.ParseInterval;
var promises = [];
console.log('%d - %d', from, from + interval);
for(from; from < to; from += interval) {
promises.push(new Promise((resolve, reject) => {
var scoped = from;
this.data.query(collection, {[index]: { $gte: from, $lte: from + interval}, (result) => {
for(var i = 0; i < result.length; i++)
this.sendToBuilder(result[i]);
resolve();
});
}));
}
promises.reduce((promise) => {
Promise.resolve()
});
}
The code seems to do what it should, but since the database query is asynchronous, it seems that out-of-order is a common occurrence. I do not want this to happen. I want each query and promise to execute sequentially to maintain the order of data.
I am trying the array.reduce() method to try and chain each promise to execute sequentially, but due to the nature of Promises it simply starts the promise and continues on, making them all fire simultaneously.
How can I ensure that it will execute sequentially? I don't mind delays between each promise as long as it doesn't block the actual thread.
Here's a working example in the spirit of your code using async/await:
function getData(reqId, collection, index, gte, lte) {
return new Promise((resolve, reject) => {
const delay = Math.floor(Math.random() * 2000) + 1;
console.log(`[getData ${reqId}] delay: ${delay}`);
const params = { collection, [index]: { $gte: gte, $lte: lte } };
const results = [0, 1, 2].map(r => `result ${reqId}.${r}`);
setTimeout(() => {
console.log(`[getData ${reqId}] this.data.query(${JSON.stringify(params)})`);
resolve(results);
}, delay);
});
}
async function parse(index, from, to) {
const collection = 'My Collection';
const interval = 10;
console.log(`Processing ${from} to ${to} by ${interval}:`);
for (from; from <= to; from += interval) {
const reqId = from;
console.log(`[parse] BEGIN ${reqId}`);
const results = await getData(reqId, collection, index, from, from + interval);
results.forEach(result => {
console.log(`[parse - awaited ${reqId}] this.sendToBuilder(${result})`);
});
console.log(`[parse] END ${reqId}`);
}
}
parse('idx', 200, 250);

How to chain promises in for loop in vanilla javascript

If I am doing an async call like the following, how can chain them with promises, so i can do stuff in order? In this example, what ends up happening is that arr will push the items out of order. I'd prefer an answer with promises, but anything will do as long as it works
var fbArrOfAlbumNames = ['Profile Pictures', 'Cover Photos', 'Mobile Uploads'];
var arr = [];
for(var x = 0; x < fbArrOfAlbumNames.length; x++) {
(function(cntr) {
FB.api('/' + fbArrOfAlbumNames[cntr] + '/photos/', {fields: 'picture,album'}, function(response) {
arr.push(response);
}
})(x);
}
Assuming your ajax calls can actually be run in parallel and you just want the results in order, then you can promisify the ajax function and use Promise.all() to get all the results in order:
// promisify the ajax call
function fbAPIPromise(path, args) {
return new Promise(function(resolve, reject) {
FB.api(path, args, function(results) {
if (!result) return resolve(null);
if (result.error) return reject(result.error);
resolve(result);
});
});
}
var promises = [];
for (var x = 0; x < 10; x++) {
promises.push(fbAPIPromise('/' + fbArrOfAlbumNames[x] + '/photos/', {fields: 'picture,album'});
}
Promise.all(promises).then(function(results) {
// results is an array of results in original order
}).catch(function(err) {
// an error occurred
});

Node.js, wait for all Redis queries to complete before continuing with execution

I need to go through an array of values, look up date in Redis (to see if it exists), and then continue. For example:
var to_check = [ 1, 2, 3 ]
var found_elements = []
for (var i = 0; i < to_check.length; i++) {
redis.EXISTS('namespace:' + to_check.length[i], function(err, value) {
if (!err && value) {
found_elements.push(to_check.length[i])
}
})
}
console.log(found_elements.join(', '))
I need to get the last line executed after all callbacks sent to Redis have been executed. What would be the best way to approach this?
Use Promise to handle complex async operations. Parallel execution is one of them.
var to_check = [ 1, 2, 3 ];
var found_elements = [];
Promise.all(to_check.map(function(item){
return new Promise(function(resolve,reject){
redis.EXISTS('namespace:' + item, function(err, value) {
if(err){
return reject(err);
}
if (value) {
found_elements.push(item);
}
resolve();
})
});
})).then(function(){
console.log('All operations are done');
}).catch(function(err){
console.log(err);
});
I am sure there are other ways. But this should work(not tested):
var to_check = [ 1, 2, 3 ]
var found_elements = []
for (var i = 0; i < to_check.length; i++) {
(function(i){
redis.EXISTS('namespace:' + to_check.length[i], function(err, value) {
if (!err && value) {
found_elements.push(to_check.length[i])
}
if(i == (to_check.length-1)){
console.log(found_elements.join(', '))
}
})
})(i);
}

Categories

Resources